2009-04-25 76 views
20

我做的GDI + .NET中大量的图像处理在ASP.NET应用程序。为什么Image.FromFile有时会使文件句柄保持打开状态?

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

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

  • 注:我没有做任何愚蠢的事喜欢养躺在附近的Image对象 - 即使我是我woudlnt预计文件句柄保持活跃
+0

您确定FromFile正在那么做吗?傻,我知道,但你可以使用句柄(SysInternal utility)来验证句柄确实来自FromFile。 – 2009-04-25 12:35:43

回答

35

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

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

  2. 使用一个FileStream和Image.FromStream方法作品,并释放。如果您上的FileStream调用Dispose()或包裹整个事情在使用{}语句所建议的克里斯文件句柄。但是,如果您尝试将Image对象保存到流中,则Image.Save方法将引发异常“GDI中发生的一般错误”。据推测,Save方法中的某些东西想知道原始文件。

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

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

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

你就可以自由操纵它(原始文件),你认为合适。

0

我会点我手指在垃圾收集器。如果你受到垃圾收集的束缚,留下它并不是真正的问题。

这家伙也有类似的complaint ...,他发现使用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; 
    } 

...

这似乎是一个完整的劈...

+0

frick是downvote为什么? – ojblass 2009-05-05 03:32:26

+0

也许downvote是“看起来像一个完整的黑客”? (不是我)。无论如何,这不是一个黑客。图像确实保持文件打开,所以如果你想断开连接,你必须创建自己的副本。打开内存流是一种方法。将图像渲染到另一个图像是另一个。无论哪种方式,你都不能依赖图像从手柄断开连接 - 它保留在它上面。 :( – Mordachai 2012-03-20 21:04:50

14

我有同样的问题,使出阅读使用

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

+2

不知道为什么我的答案被拒绝了,它会在没有保持句柄打开的情况下读取 – Steven 2009-04-25 12:37:44

1

使文件确定你正在处置。

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

的使用表达在Image.Dispose的情况下简写

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

@Rex它调用GdipDisposeImage的extern /机Win32呼叫在它的处置()。

IDisposable的是作为一种机制来释放非托管资源(哪个文件句柄)

+0

在Image的情况下,Dispose实际上做了什么?它是否释放文件系统句柄,非托管内存等? – 2009-04-30 02:38:20

4

Image.FromFile保持,直到图像设置在文件处理打开。从MSDN

“的文件保持锁定,直到该图片被设置”。

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

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

编辑:(一年稍后)

上面的代码是危险的,因为它是不可预测的,在某个时间点(关闭FILESTREAM之后)你可以得到可怕的“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; 
+0

我认为FileStream也需要清理。 – 2009-04-30 02:31:30

+0

绝对需要清理。感谢您修复帖子。 – 2009-04-30 15:54:55

0

如上所述,微软解决了几个图像加载后导致GDI +错误。如上由史蒂芬提到我的VB的解决方案是

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

我也尝试了所有的技巧(ReadAllBytes文件流=> FromStream => newBitmap()进行复制等),他们所有的工作。不过,我想知道,如果你能找到的东西短,

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

似乎工作,也因为其配置文件处理以及原始图像输入对象,并创建一个新的位图对象,即独立于原始文件,因此可以无误地保存到流或文件中。

0

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

在我的情况的解决办法是尽快拨打‘.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() 
相关问题