codefinger
asked on
avoiding "file in use by another process" during file deletion
I have two web services that allow a multipage tiff file to be uploaded one page at a time.
StoreTiffPages(identifier, pagenumber) and
StoreTiffFinal(identifier)
StoreTiffPages is called for each page in the file and the pages received are stored in a temporary directory on the server until StoreTiffFinal is called
StoreTiffFinal assembles the files into a single multipage tiff file and moves it to its final location on the server. Then it is supposed to delete the temporary files, however, what frequently happens instead is an error "the process cannot access the file xxxx because it is being used by another process."
Below is the code I use to assemble the multipage tiff. What do I have to do to free up the temporary files for deletion?
Function ToMultiPageTiff(ByVal files_list() As String, ByVal ls_destination_filename As String, ByRef as_errmsg As String) As Boolean
Try
Dim enc As Encoder
'get codec info
enc = Encoder.SaveFlag
Dim info As System.Drawing.Imaging.Ima geCodecInf o
info = Nothing
Dim ice As System.Drawing.Imaging.Ima geCodecInf o
For Each ice In System.Drawing.Imaging.Ima geCodecInf o.GetImage Encoders
If ice.MimeType = "image/tiff" Then
info = ice
End If
Next
Dim masterimg As Bitmap = Nothing
masterimg = CType(Image.FromFile(files _list(0)), Bitmap)
Dim ep As System.Drawing.Imaging.Enc oderParame ters
ep = New System.Drawing.Imaging.Enc oderParame ters(2)
ep.Param(0) = New EncoderParameter(enc, CType(EncoderValue.MultiFr ame, Long))
ep.Param(1) = New EncoderParameter(enc.Compr ession, CType(EncoderValue.Compres sionLZW, Long))
'Save the first image. This is the image to which all subsequent images will be added.
masterimg.Save(ls_destinat ion_filena me, info, ep)
'Add the remaining images (these next two lines are probably unneccessary)
ep.Param(0) = New EncoderParameter(enc, CType(EncoderValue.FrameDi mensionPag e, Long))
ep.Param(1) = New EncoderParameter(enc.Compr ession, CType(EncoderValue.Compres sionLZW, Long))
Dim li_cntr As Integer
For li_cntr = 1 To files_list.Length - 1
masterimg.SaveAdd(Image.Fr omFile(fil es_list(li _cntr)), ep)
Next
ep.Param(0) = New EncoderParameter(enc, CType(EncoderValue.Flush, Long))
masterimg.SaveAdd(ep)
Return True
Catch ex As Exception
as_errmsg = ex.Message
Return False
End Try
End Function
Thanks in advance for your help.
StoreTiffPages(identifier,
StoreTiffFinal(identifier)
StoreTiffPages is called for each page in the file and the pages received are stored in a temporary directory on the server until StoreTiffFinal is called
StoreTiffFinal assembles the files into a single multipage tiff file and moves it to its final location on the server. Then it is supposed to delete the temporary files, however, what frequently happens instead is an error "the process cannot access the file xxxx because it is being used by another process."
Below is the code I use to assemble the multipage tiff. What do I have to do to free up the temporary files for deletion?
Function ToMultiPageTiff(ByVal files_list() As String, ByVal ls_destination_filename As String, ByRef as_errmsg As String) As Boolean
Try
Dim enc As Encoder
'get codec info
enc = Encoder.SaveFlag
Dim info As System.Drawing.Imaging.Ima
info = Nothing
Dim ice As System.Drawing.Imaging.Ima
For Each ice In System.Drawing.Imaging.Ima
If ice.MimeType = "image/tiff" Then
info = ice
End If
Next
Dim masterimg As Bitmap = Nothing
masterimg = CType(Image.FromFile(files
Dim ep As System.Drawing.Imaging.Enc
ep = New System.Drawing.Imaging.Enc
ep.Param(0) = New EncoderParameter(enc, CType(EncoderValue.MultiFr
ep.Param(1) = New EncoderParameter(enc.Compr
'Save the first image. This is the image to which all subsequent images will be added.
masterimg.Save(ls_destinat
'Add the remaining images (these next two lines are probably unneccessary)
ep.Param(0) = New EncoderParameter(enc, CType(EncoderValue.FrameDi
ep.Param(1) = New EncoderParameter(enc.Compr
Dim li_cntr As Integer
For li_cntr = 1 To files_list.Length - 1
masterimg.SaveAdd(Image.Fr
Next
ep.Param(0) = New EncoderParameter(enc, CType(EncoderValue.Flush, Long))
masterimg.SaveAdd(ep)
Return True
Catch ex As Exception
as_errmsg = ex.Message
Return False
End Try
End Function
Thanks in advance for your help.
ASKER
Syntax please? Remember, all I have to work with during this process is
strings.
Image.FromFile(ls_filename )
Thanks.
strings.
Image.FromFile(ls_filename
Thanks.
masterimg.Dispose()
Bob
Bob
ASKER
masterimg.Dispose() did not prevent the problem.
Note, the file complained about (xxxx) is the first of the temporary files.
(Sorry, should have specified that sooner.)
Should I also invoke garbage collection and/or waitforpendingfinalizers?
Thanks.
Note, the file complained about (xxxx) is the first of the temporary files.
(Sorry, should have specified that sooner.)
Should I also invoke garbage collection and/or waitforpendingfinalizers?
Thanks.
Where do you create the temporary files? It should be as simple as File.Close(), but it's hard to tell from what you've shown. Since garbage collection in non-deterministic (you can't tell when it will happen next), GC.Collect() will force it to happen when you want. Just remember that forcing garbage collection can be very detrimental to your application depending on how it is used.
Bob
Bob
ASKER
All code for StoreTiffPages and StoreTiffFinal is shown below. Note xfilestream.close() after each temporary file is created.
Public Function StoreTiffPages(ByVal iBinStream As String, _
ByVal iDirectory As String, _
ByVal iName As String, _
ByVal iPageNum As String, _
ByVal iOptions As String _
) As doStoreTiffPages.Result
Dim xResult As New doStoreTiffPages.Result()
'>>> Do work here and set results...
Dim lfilelen As Long
Dim fs_new_file_name As String
Dim ls_dirname As String
Dim fs_64 As String
Dim ls_splitname() As String
Dim upperbound As Integer
Dim ls_tempfilepath As String
Dim fs_temp_file_name As String
Dim ls_current_filename As String
Dim ls_last_version_no As String
Dim lb_file_exists As Boolean
lb_file_exists = False
Dim ls_ext As String
Dim ls_arrayfilenames As String
ls_splitname = Split(iName, ".")
upperbound = UBound(ls_splitname)
ls_ext = ls_splitname(upperbound)
ls_dirname = System.Configuration.Confi gurationSe ttings.App Settings(" MAINDIR") + iDirectory
ls_tempfilepath = System.Configuration.Confi gurationSe ttings.App Settings(" MAINDIR") + iDirectory + "\TEMPFILES"
fs_temp_file_name = ls_tempfilepath + "\" + "_" + CStr(iPageNum) + "_" + iName
Try
Dim xDoc As New MSXML2.DOMDocument()
Dim xnode As MSXML2.IXMLDOMNode
Dim xbase64String As String
iBinStream = iBinStream.Replace("dt:", "")
xDoc.loadXML(iBinStream)
xnode = xDoc.selectSingleNode("DOC UMENT/FILE ")
xbase64String = xnode.text
xbase64String = xbase64String.Replace(" ", "+")
xDoc = Nothing
xnode = Nothing
Dim xByteArray() As Byte
xByteArray = System.Convert.FromBase64S tring(xbas e64String)
If System.IO.File.Exists(fs_t emp_file_n ame) Then
System.IO.File.Delete(fs_t emp_file_n ame)
End If
Dim xFileStream As New _
System.IO.FileStream(fs_te mp_file_na me, System.IO.FileMode.Create, FileAccess.ReadWrite)
Dim xBinWrite As System.IO.BinaryWriter = New System.IO.BinaryWriter(xFi leStream)
xBinWrite.Write(xByteArray , 0, xByteArray.Length)
xBinWrite.Flush()
xBinWrite.Close()
xFileStream.Close()
xResult.Stored = True
xResult.Message = "Page stored to temporary directory. Expecting additional pages or notice of completion."
xResult.Exception.Code = 0
Return xResult
Catch ex As Exception
xResult.Message = "StoreTiffPages failed: " + ex.Message
xResult.Exception.Msg = ex.Message
xResult.Exception.Code = -1
Return xResult
End Try
End Function
Function StoreTiffFinal(ByVal iName As String, ByVal iDirectory As String, ByVal ioverwrite As String, ByVal ioptions As String)
Dim xResult As New doStoreTiffFinal.Result()
Try
Dim ls_tempfilepath As String
Dim ls_dirname As String
ls_dirname = System.Configuration.Confi gurationSe ttings.App Settings(" MAINDIR") + iDirectory
ls_tempfilepath = ls_dirname + "\TEMPFILES"
Dim tiffpagesarray() As Image
tiffpagesarray = MakeTiffArray(iName)
Dim ls_filelist As String()
ls_filelist = MakeSortedArrayofFileNames (iName)
Dim ls_destfilename As String
ls_destfilename = ls_tempfilepath + "\" + iName
Dim ls_errmsg As String
If Not ToMultiPageTiff(ls_filelis t, ls_destfilename, ls_errmsg) Then
xResult.Exception.Code = -1
xResult.Exception.Msg = ls_errmsg
xResult.Message = ls_errmsg
Return xResult
End If
Dim fs_new_file_name As String
Dim ls_last_version_no As String
Dim lb_file_exists As Boolean
Dim ls_current_filename As String
lb_file_exists = True
Dim ls_ext As String
ls_ext = "tif"
'now move this final temporary file to permanent storage...
ls_last_version_no = GetLastVersionNo(iDirector y, iName)
If ls_last_version_no = "" Then
lb_file_exists = False
End If
Dim ls_splitfile() As String
If lb_file_exists Then
ls_splitfile = Split(iName, ".")
ls_current_filename = ls_splitfile(0) + "V" + ls_last_version_no + "." + ls_ext
If UCase(ioverwrite) = "TRUE" Then
If System.IO.File.Exists(ls_d irname + "\" + ls_current_filename) Then
System.IO.File.Delete(ls_d irname + "\" + ls_current_filename)
End If
End If
End If
fs_new_file_name = ls_dirname + "\" + iName
fs_new_file_name = GetNewFileName(fs_new_file _name)
fs_new_file_name = UCase(fs_new_file_name)
System.IO.File.Move(ls_des tfilename, fs_new_file_name)
'Delete the temporary files
Dim splitfilename() As String
Dim leftside As String
Dim rightside As String
Dim ls_pattern As String
Dim filelist() As String
Dim li_lastfile As Integer
Dim ls_lastfile As String
ls_dirname = System.Configuration.Confi gurationSe ttings.App Settings(" MAINDIR")
ls_dirname = ls_dirname + iDirectory + "\TEMPFILES"
ls_pattern = "*" + iName
filelist = System.IO.Directory.GetFil es(ls_dirn ame, ls_pattern)
Dim cntr As Integer
For cntr = 0 To filelist.Length - 1
Dim fi As FileInfo
fi = New FileInfo(filelist(cntr))
If fi.Exists Then
fi.Delete()
End If
'System.IO.File.Delete(fil elist(cntr ))
Next
xResult.Stored = True
xResult.Message = "New image has been stored to OCW server."
xResult.Exception.Code = 0
Catch ex As Exception
xResult.Stored = False
xResult.Message = ex.Message
xResult.Exception.Code = -1
End Try
Return xResult
End Function
Public Function StoreTiffPages(ByVal iBinStream As String, _
ByVal iDirectory As String, _
ByVal iName As String, _
ByVal iPageNum As String, _
ByVal iOptions As String _
) As doStoreTiffPages.Result
Dim xResult As New doStoreTiffPages.Result()
'>>> Do work here and set results...
Dim lfilelen As Long
Dim fs_new_file_name As String
Dim ls_dirname As String
Dim fs_64 As String
Dim ls_splitname() As String
Dim upperbound As Integer
Dim ls_tempfilepath As String
Dim fs_temp_file_name As String
Dim ls_current_filename As String
Dim ls_last_version_no As String
Dim lb_file_exists As Boolean
lb_file_exists = False
Dim ls_ext As String
Dim ls_arrayfilenames As String
ls_splitname = Split(iName, ".")
upperbound = UBound(ls_splitname)
ls_ext = ls_splitname(upperbound)
ls_dirname = System.Configuration.Confi
ls_tempfilepath = System.Configuration.Confi
fs_temp_file_name = ls_tempfilepath + "\" + "_" + CStr(iPageNum) + "_" + iName
Try
Dim xDoc As New MSXML2.DOMDocument()
Dim xnode As MSXML2.IXMLDOMNode
Dim xbase64String As String
iBinStream = iBinStream.Replace("dt:", "")
xDoc.loadXML(iBinStream)
xnode = xDoc.selectSingleNode("DOC
xbase64String = xnode.text
xbase64String = xbase64String.Replace(" ", "+")
xDoc = Nothing
xnode = Nothing
Dim xByteArray() As Byte
xByteArray = System.Convert.FromBase64S
If System.IO.File.Exists(fs_t
System.IO.File.Delete(fs_t
End If
Dim xFileStream As New _
System.IO.FileStream(fs_te
Dim xBinWrite As System.IO.BinaryWriter = New System.IO.BinaryWriter(xFi
xBinWrite.Write(xByteArray
xBinWrite.Flush()
xBinWrite.Close()
xFileStream.Close()
xResult.Stored = True
xResult.Message = "Page stored to temporary directory. Expecting additional pages or notice of completion."
xResult.Exception.Code = 0
Return xResult
Catch ex As Exception
xResult.Message = "StoreTiffPages failed: " + ex.Message
xResult.Exception.Msg = ex.Message
xResult.Exception.Code = -1
Return xResult
End Try
End Function
Function StoreTiffFinal(ByVal iName As String, ByVal iDirectory As String, ByVal ioverwrite As String, ByVal ioptions As String)
Dim xResult As New doStoreTiffFinal.Result()
Try
Dim ls_tempfilepath As String
Dim ls_dirname As String
ls_dirname = System.Configuration.Confi
ls_tempfilepath = ls_dirname + "\TEMPFILES"
Dim tiffpagesarray() As Image
tiffpagesarray = MakeTiffArray(iName)
Dim ls_filelist As String()
ls_filelist = MakeSortedArrayofFileNames
Dim ls_destfilename As String
ls_destfilename = ls_tempfilepath + "\" + iName
Dim ls_errmsg As String
If Not ToMultiPageTiff(ls_filelis
xResult.Exception.Code = -1
xResult.Exception.Msg = ls_errmsg
xResult.Message = ls_errmsg
Return xResult
End If
Dim fs_new_file_name As String
Dim ls_last_version_no As String
Dim lb_file_exists As Boolean
Dim ls_current_filename As String
lb_file_exists = True
Dim ls_ext As String
ls_ext = "tif"
'now move this final temporary file to permanent storage...
ls_last_version_no = GetLastVersionNo(iDirector
If ls_last_version_no = "" Then
lb_file_exists = False
End If
Dim ls_splitfile() As String
If lb_file_exists Then
ls_splitfile = Split(iName, ".")
ls_current_filename = ls_splitfile(0) + "V" + ls_last_version_no + "." + ls_ext
If UCase(ioverwrite) = "TRUE" Then
If System.IO.File.Exists(ls_d
System.IO.File.Delete(ls_d
End If
End If
End If
fs_new_file_name = ls_dirname + "\" + iName
fs_new_file_name = GetNewFileName(fs_new_file
fs_new_file_name = UCase(fs_new_file_name)
System.IO.File.Move(ls_des
'Delete the temporary files
Dim splitfilename() As String
Dim leftside As String
Dim rightside As String
Dim ls_pattern As String
Dim filelist() As String
Dim li_lastfile As Integer
Dim ls_lastfile As String
ls_dirname = System.Configuration.Confi
ls_dirname = ls_dirname + iDirectory + "\TEMPFILES"
ls_pattern = "*" + iName
filelist = System.IO.Directory.GetFil
Dim cntr As Integer
For cntr = 0 To filelist.Length - 1
Dim fi As FileInfo
fi = New FileInfo(filelist(cntr))
If fi.Exists Then
fi.Delete()
End If
'System.IO.File.Delete(fil
Next
xResult.Stored = True
xResult.Message = "New image has been stored to OCW server."
xResult.Exception.Code = 0
Catch ex As Exception
xResult.Stored = False
xResult.Message = ex.Message
xResult.Exception.Code = -1
End Try
Return xResult
End Function
As long as you are doing GC.Collect thousands of times, it should actually help you in what appears to be a timing issue.
Bob
Bob
ASKER
>>As long as you are doing GC.Collect thousands of times, it should actually help you in what appears to be a timing issue.<<
I hope you meant AREN'T doing GC.Collect thousands of times ;)
I hope you meant AREN'T doing GC.Collect thousands of times ;)
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Still no joy.
Added System.GC.Collect right after xstream.close() in the StoreTiffPages function.
Before, I could run the upload process twice in a row if they were run far enough apart (like about 30-45 minutes), but adding System.GC.Collect seems to have made the problem worse. Now it won't even run once and gives the same error.
Added System.GC.Collect right after xstream.close() in the StoreTiffPages function.
Before, I could run the upload process twice in a row if they were run far enough apart (like about 30-45 minutes), but adding System.GC.Collect seems to have made the problem worse. Now it won't even run once and gives the same error.
I don't think that I am getting a clear picture here, since there aren't any comments and I didn't quite get what you are talking about.
Bob
Bob
ASKER
Nevermind my last comment, it only confused the issue.
Here is some additionial info that may help:
http://www.dotnet247.com/247reference/msgs/13/66280.aspx
A System.Drawing.Image object has a tempting FromFile method that
allows you to pass in a full path to fill the object. There is a
significant downside to this simple method: It locks the image file a
lot longer than you'd ever want, prohibiting you to delete or modify
the source file out side of your app after you opened it (any such
attempts raise a "There has been a sharing violation. The source or
destination file may be in use" error).
Using the FromFile in a WinForm app will lock the file for the
lifetime of the application, even if you let the Image fall out of
scope and, worse still, in ASP.net passing a UNC to FromFile to open
an image located remotely will lock the file indefinitely (even hours
beyond the run of the WebApp, that can't be the Garbage Collector!).
I don't know exactly why this is, but I can tell you a workaround:
Don't use Image.FromFile. Instead, use a Stream object open the file,
pass it to the Image's FromStream method and close the stream when the
Image object is filled.
Dim objStream As System.IO.Stream = System.IO.File.Open([Path To
File], System.IO.FileMode.Open)
Dim MyImage as System.Drawing.Image
MyImage.FromStream(objStre am)
objStream.Close()
I have tried using fromstream as the article suggests, but the problem still persists.
Next step is to involve bytearrays. I'll let you know how it goes.
Here is some additionial info that may help:
http://www.dotnet247.com/247reference/msgs/13/66280.aspx
A System.Drawing.Image object has a tempting FromFile method that
allows you to pass in a full path to fill the object. There is a
significant downside to this simple method: It locks the image file a
lot longer than you'd ever want, prohibiting you to delete or modify
the source file out side of your app after you opened it (any such
attempts raise a "There has been a sharing violation. The source or
destination file may be in use" error).
Using the FromFile in a WinForm app will lock the file for the
lifetime of the application, even if you let the Image fall out of
scope and, worse still, in ASP.net passing a UNC to FromFile to open
an image located remotely will lock the file indefinitely (even hours
beyond the run of the WebApp, that can't be the Garbage Collector!).
I don't know exactly why this is, but I can tell you a workaround:
Don't use Image.FromFile. Instead, use a Stream object open the file,
pass it to the Image's FromStream method and close the stream when the
Image object is filled.
Dim objStream As System.IO.Stream = System.IO.File.Open([Path To
File], System.IO.FileMode.Open)
Dim MyImage as System.Drawing.Image
MyImage.FromStream(objStre
objStream.Close()
I have tried using fromstream as the article suggests, but the problem still persists.
Next step is to involve bytearrays. I'll let you know how it goes.
ASKER
I think I was applying all the suggested fixes to the wrong function. I was able to fix the problem by restoring an older version of the code. Most likely one of your suggestions would have worked if I had applied it correctly, so I am awarding the points to you and giving an A grade. You were also the only one who even tried to answer the question and I am sure if I had kept asking, you would have kept trying until the problem was solved. Thank you for your help and your patience!
Bob