我在 ASP.NET 应用程序中的 .NET 中的 GDI+ 中进行了大量图像处理。
我经常发现 Image.FromFile() 保持文件句柄打开。
为什么是这样?在不保留文件句柄的情况下打开图像的最佳方法是什么。
- 注意:我没有做任何愚蠢的事情,比如让 Image 对象到处乱放——即使我是,我也不希望文件句柄保持活动状态
我在 ASP.NET 应用程序中的 .NET 中的 GDI+ 中进行了大量图像处理。
我经常发现 Image.FromFile() 保持文件句柄打开。
为什么是这样?在不保留文件句柄的情况下打开图像的最佳方法是什么。
我经历了与该线程上的其他一些海报相同的旅程。我注意到的事情:
在释放文件句柄时使用 Image.FromFile 似乎是不可预测的。在所有情况下,调用 Image.Dispose() 都不会释放文件句柄。
如果您在 FileStream 上调用 Dispose() 或按照 Kris 的建议将整个内容包装在 Using {} 语句中,则使用 FileStream 和 Image.FromStream 方法可以工作,并释放文件的句柄。但是,如果您随后尝试将 Image 对象保存到流中,Image.Save 方法将引发异常"A generic error occurred in GDI+"。大概 Save 方法中的某些内容想了解原始文件。
史蒂文的方法对我有用。我能够删除内存中带有 Image 对象的原始文件。我还能够将图像保存到流和文件中(我需要做这两件事)。我还能够保存到与原始文件同名的文件中,如果您使用 Image.FromFile 方法,这是不可能的(我觉得这很奇怪,因为这肯定是最有可能的用例,但是嘿.)
所以总结一下,像这样打开你的图像:
Image img = Image.FromStream(new MemoryStream(File.ReadAllBytes(path)));
然后,您可以随意操作它(以及原始文件)。
我有同样的问题,并诉诸使用读取文件
return Image.FromStream(new MemoryStream(File.ReadAllBytes(fileName)));
Image.FromFile 保持文件句柄打开,直到图像被释放。从MSDN:
“在处理图像之前,文件保持锁定状态。”
使用 Image.FromStream,你就不会有问题了。
using(var fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
return Image.FromStream(fs);
}
编辑:(一年后)
上面的代码是危险的,因为它是不可预测的,在某个时间点(关闭文件流之后)你可能会得到可怕的"A generic error occurred in 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")) {}
using 表达式是
IDisposable obj;
try { }
finally
{
obj.Dispose();
}
@Rex 在 Image.Dispose 的情况下,它在 Dispose() 中调用 GdipDisposeImage extern / 本机 Win32 调用。
IDisposable 用作释放非托管资源的机制(哪些文件句柄是)
我还尝试了您的所有提示(ReadAllBytes、FileStream=>FromStream=>newBitmap() 来制作副本等),它们都奏效了。但是,我想知道,如果你能找到更短的东西,并且
using (Image temp = Image.FromFile(path))
{
return new Bitmap(temp);
}
似乎也可以工作,因为它处理文件句柄以及原始图像对象并创建一个新的位图对象,该对象独立于原始文件,因此可以保存到流或文件中而不会出错。
我不得不将手指指向垃圾收集器。如果您受到垃圾收集的摆布,那么离开它并不是真正的问题。
这个人也有类似的抱怨……他找到了一种使用 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;
}
...
这似乎是一个完整的黑客......
如上所述,Microsoft 解决方法会在加载多个图像后导致 GDI+ 错误。Steven 上面提到的我的 VB 解决方案是
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
在我的情况下,解决方案是为每个图像调用“.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()