22

我在 ASP.NET 应用程序中的 .NET 中的 GDI+ 中进行了大量图像处理。

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

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

  • 注意:我没有做任何愚蠢的事情,比如让 Image 对象到处乱放——即使我是,我也不希望文件句柄保持活动状态
4

8 回答 8

37

我经历了与该线程上的其他一些海报相同的旅程。我注意到的事情:

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

  2. 如果您在 FileStream 上调用 Dispose() 或按照 Kris 的建议将整个内容包装在 Using {} 语句中,则使用 FileStream 和 Image.FromStream 方法可以工作,并释放文件的句柄。但是,如果您随后尝试将 Image 对象保存到流中,Image.Save 方法将引发异常"A generic error occurred in GDI+"。大概 Save 方法中的某些内容想了解原始文件。

  3. 史蒂文的方法对我有用。我能够删除内存中带有 Image 对象的原始文件。我还能够将图像保存到流和文件中(我需要做这两件事)。我还能够保存到与原始文件同名的文件中,如果您使用 Image.FromFile 方法,这是不可能的(我觉得这很奇怪,因为这肯定是最有可能的用例,但是嘿.)

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

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

然后,您可以随意操作它(以及原始文件)。

于 2009-07-09T17:23:03.627 回答
16

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

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

于 2009-04-25T06:45:09.800 回答
5

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;
于 2009-04-25T16:20:24.923 回答
1

确保您正确处置。

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

using 表达式是

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

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

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

于 2009-04-25T06:53:25.847 回答
1

我还尝试了您的所有提示(ReadAllBytes、FileStream=>FromStream=>newBitmap() 来制作副本等),它们都奏效了。但是,我想知道,如果你能找到更短的东西,并且

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

似乎也可以工作,因为它处理文件句柄以及原始图像对象并创建一个新的位图对象,该对象独立于原始文件,因此可以保存到流或文件中而不会出错。

于 2011-04-25T11:20:39.307 回答
0

我不得不将手指指向垃圾收集器。如果您受到垃圾收集的摆布,那么离开它并不是真正的问题。

这个人也有类似的抱怨……他找到了一种使用 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;
    }

...

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

于 2009-04-25T06:32:31.063 回答
0

如上所述,Microsoft 解决方法会在加载多个图像后导致 GDI+ 错误。Steven 上面提到的我的 VB 解决方案是

picTemp.Image = Image.FromStream(New System.IO.MemoryStream(My.Computer.FileSystem.ReadAllBytes(strFl)))
于 2009-07-12T20:13:13.413 回答
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()
于 2015-12-06T02:43:53.297 回答