为什么Image.FromFile有时会保持文件句柄打开?

我在ASP.NET应用程序中使用.NET中的GDI +进行大量image processing。

我经常发现Image.FromFile()保持打开文件句柄。

为什么是这样? 没有保留文件句柄的情况下打开图像的最佳方法是什么?

  • 注意:我没有做任何愚蠢的事情,比如让Image对象留在周围 – 即使我不想让文件句柄保持活动状态

我在这个主题上和其他一些海报一样经历了同样的旅程。 我注意到的事情:

  1. 使用Image.FromFile在释放文件句柄时似乎是不可预知的。 调用Image.Dispose()在所有情况下都不释放文件句柄。

  2. 使用FileStream和Image.FromStream方法工作,如果在FileStream上调用Dispose()或在Kris推荐的Using {}语句中包装整个事件,则释放文件上的句柄。 但是,如果您尝试将Image对象保存到stream中,则Image.Save方法将引发一个exception“GDI +中发生了一般性错误” 。 据推测,Save方法中的某些东西想知道原始文件。

  3. 史蒂文的方法为我工作。 我能够删除内存中的图像对象的原始文件。 我也能够保存图像到一个stream和一个文件(我需要做这两件事)。 我也能够保存到与原始文件名称相同的文件,如果您使用Image.FromFile方法logging为不可能的东西(我觉得这很奇怪,因为这肯定是最可能的用例,但是嘿嘿。)

所以总结一下,像这样打开你的图片:

Image img = Image.FromStream(new MemoryStream(File.ReadAllBytes(path))); 

然后,您可以自由地操纵它(和原始文件),只要你认为合适。

我有同样的问题,并诉诸阅读文件使用

return Image.FromStream(new MemoryStream(File.ReadAllBytes(fileName)));

Image.FromFile保持文件句柄打开,直到图像被丢弃。 从MSDN :

“该文件保持locking,直到图像被丢弃。”

使用Image.FromStream,你不会有这个问题。

 using(var fs = new FileStream(filename, FileMode.Open, FileAccess.Read)) { return Image.FromStream(fs); } 

编辑:(一年,稍后)

上面的代码是危险的,因为它是不可预知的,在某些时候(closures文件stream后),您可能会遇到“GDI +中发生的一般错误” 。 我会修改它:

 Image tmpImage; Bitmap returnImage; using(var fs = new FileStream(filename, FileMode.Open, FileAccess.Read)) { tmpImage = Image.FromStream(fs); returnImage = new Bitmap(tmpImage); tmpImage.Dispose(); } return returnImage; 

确保你正确处置。

 using (Image.FromFile("path")) {} 

使用expression式是简写

 IDisposable obj; try { } finally { obj.Dispose(); } 

在Image.Dispose的情况下@Rex调用它的Dispose()调用GdipDisposeImage extern /本地Win32调用。

IDisposable被用作释放非托pipe资源的机制(哪个文件句柄是)

我也尝试了所有的技巧(ReadAllBytes,FileStream => FromStream => newBitmap()来创build一个副本等),他们都工作。 不过,我想知道,如果你能find更短的东西,

 using (Image temp = Image.FromFile(path)) { return new Bitmap(temp); } 

似乎也工作,因为它处理文件句柄以及原始的图像对象,并创build一个新的位图对象,这是独立于原始文件,因此可以保存到一个stream或文件没有错误。

我不得不把我的手指指向垃圾收集器。 如果你处在垃圾收集的摆布之中,那么留下它并不是真正的问题。

这家伙有一个类似的抱怨 …他find了一个使用FileStream对象,而不是直接从文件中加载的解决方法。

 public static Image LoadImageFromFile(string fileName) { Image theImage = null; fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); { byte[] img; img = new byte[fileStream.Length]; fileStream.Read(img, 0, img.Length); fileStream.Close(); theImage = Image.FromStream(new MemoryStream(img)); img = null; } 

这似乎是一个完整的黑客…

正如上面提到的,微软在几个图片加载之后,会导致GDI +错误。 对于我如上所述的史蒂文VB的解决scheme是

 picTemp.Image = Image.FromStream(New System.IO.MemoryStream(My.Computer.FileSystem.ReadAllBytes(strFl))) 

我刚刚遇到了同样的问题,我试图将多个单页TIFF文件合并为一个多部分TIFF图像。 我需要使用Image.Save()和'Image.SaveAdd()`: https ://msdn.microsoft.com/en-us/library/windows/desktop/ms533839%28v=vs.85%29.aspx

在我的情况下,解决scheme是一旦我完成了对每个图像调用“.Dispose()”:

 ' Iterate through each single-page source .tiff file Dim initialTiff As System.Drawing.Image = Nothing For Each filePath As String In srcFilePaths Using fs As System.IO.FileStream = File.Open(filePath, FileMode.Open, FileAccess.Read) If initialTiff Is Nothing Then ' ... Save 1st page of multi-part .TIFF initialTiff = Image.FromStream(fs) encoderParams.Param(0) = New EncoderParameter(Encoder.Compression, EncoderValue.CompressionCCITT4) encoderParams.Param(1) = New EncoderParameter(Encoder.SaveFlag, EncoderValue.MultiFrame) initialTiff.Save(outputFilePath, encoderInfo, encoderParams) Else ' ... Save subsequent pages Dim newTiff As System.Drawing.Image = Image.FromStream(fs) encoderParams = New EncoderParameters(2) encoderParams.Param(0) = New EncoderParameter(Encoder.Compression, EncoderValue.CompressionCCITT4) encoderParams.Param(1) = New EncoderParameter(Encoder.SaveFlag, EncoderValue.FrameDimensionPage) initialTiff.SaveAdd(newTiff, encoderParams) newTiff.Dispose() End If End Using Next ' Make sure to close the file initialTiff.Dispose() 
    Interesting Posts