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

时间:2009-04-25 06:18:57

标签: gdi+ filehandle

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

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

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

  • NB:我没有做任何愚蠢的事情,比如保持Image对象的位置 - 即使我是,我也不希望文件句柄保持活动状态

8 个答案:

答案 0 :(得分:35)

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

  1. 使用Image.FromFile在发布文件句柄时似乎无法预测。 在所有情况下调用Image.Dispose()都没有释放文件句柄。

  2. 使用FileStream和Image.FromStream方法可以正常工作,如果在FileStream上调用Dispose()或者按照Kris的建议将整个事件包装在Using {}语句中,则释放该文件的句柄。但是,如果您然后尝试将Image对象保存到流中,则Image.Save方法将引发异常“GDI +中出现一般错误”。据推测,Save方法中的某些内容想知道原始文件。

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

  4. 总而言之,请按照以下方式打开您的图片:

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

    然后您可以根据需要自由操纵它(和原始文件)。

答案 1 :(得分:14)

我遇到了同样的问题并使用

来阅读文件

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

答案 2 :(得分:5)

Image.FromFile会保持文件句柄处于打开状态,直到处理完图像。来自MSDN

“文件保持锁定,直到图像被丢弃。”

使用Image.FromStream,您将不会遇到问题。

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

编辑(一年又一点)

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

答案 3 :(得分:1)

确保您正确处置。

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

using表达式是

的简写
IDisposable obj;
try { }
finally 
{
    obj.Dispose();
}

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

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

答案 4 :(得分:1)

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

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

似乎也可以工作,因为它处理文件句柄和原始Image对象并创建一个新的Bitmap对象,它独立于原始文件,因此可以保存到流或文件而不会出现错误

答案 5 :(得分: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;
    }

...

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

答案 6 :(得分:0)

如上所述,在加载了几个图像后,Microsoft解决了导致GDI +错误的问题。如上所述Steven的VB解决方案是

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

答案 7 :(得分: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()