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

发布于 2024-07-18 16:27:25 字数 219 浏览 5 评论 0原文

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

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

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

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

I am doing a lot of image processing in GDI+ in .NET in an ASP.NET application.

I frequently find that Image.FromFile() is keeping a file handle open.

Why is this? What is the best way to open an image without the file handle being retained.

  • NB: I'm not doing anything stupid like keeping the Image object lying around - and even if I was I woudlnt expect the file handle to be kept active

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

九歌凝 2024-07-25 16:27:26

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

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

  2. 使用 FileStream 和 Image.FromStream 方法可以工作,并且如果调用 Dispose( ) 在 FileStream 上,或者按照 Kris 的建议将整个内容包装在 using {} 语句中。 但是,如果您随后尝试将 Image 对象保存到流中,Image.Save 方法将引发异常“GDI+ 中发生一般错误”。 大概 Save 方法中的某些内容想要了解原始文件。

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

总而言之,像这样打开您的图像:

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

然后您可以按照您认为合适的方式自由操作它(以及原始文件)。

I went through the same journey as a few other posters on this thread. Things I noted:

  1. Using Image.FromFile does seem unpredictable on when it releases the file handle. Calling the Image.Dispose() did not release the file handle in all cases.

  2. Using a FileStream and the Image.FromStream method works, and releases the handle on the file if you call Dispose() on the FileStream or wrap the whole thing in a Using {} statement as recommended by Kris. However if you then attempt to save the Image object to a stream, the Image.Save method throws an exception "A generic error occured in GDI+". Presumably something in the Save method wants to know about the originating file.

  3. Steven's approach worked for me. I was able to delete the originating file with the Image object in memory. I was also able to save the Image to both a stream and a file (I needed to do both of these things). I was also able to save to a file with the same name as the originating file, something that is documented as not possible if you use the Image.FromFile method (I find this weird since surely this is the most likely use case, but hey.)

So to summarise, open your Image like this:

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

You are then free to manipulate it (and the originating file) as you see fit.

攒一口袋星星 2024-07-25 16:27:26

我遇到了同样的问题,并求助于使用

return Image.FromStream(new MemoryStream(File.ReadAllBytes(fileName)));来读取文件

I have had the same problem and resorted to reading the file using

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

揽清风入怀 2024-07-25 16:27:26

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;

Image.FromFile keeps the file handle open until the image is disposed. From the MSDN:

"The file remains locked until the Image is disposed."

Use Image.FromStream, and you won't have the problem.

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

Edit: (a year and a bit later)

The above code is dangerous as it is unpredictable, at some point in time (after closing the filestream) you may get the dreaded "A generic error occurred in GDI+". I would amend it to:

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;
断舍离 2024-07-25 16:27:26

我还尝试了您的所有技巧(ReadAllBytes、FileStream=>FromStream=>newBitmap() 进行复制等),它们都有效。 但是,我想知道,您是否可以找到更短的东西,并且

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

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

I also tried all your tips (ReadAllBytes, FileStream=>FromStream=>newBitmap() to make a copy, etc.) and they all worked. However, I wondered, if you could find something shorter, and

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

appears to work, too, as it disposes the file handle as well as the original Image-object and creates a new Bitmap-object, that is independent from the original file and therefore can be saved to a stream or file without errors.

水溶 2024-07-25 16:27:26

确保您正确处置。

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

在 Image.Dispose 的情况下, using 表达式是

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

@Rex 的简写,它在其 Dispose() 中调用 GdipDisposeImage extern / native Win32 调用。

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

Make sure you are Disposing properly.

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

The using expression is shorthand for

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

@Rex in the case of Image.Dispose it calls GdipDisposeImage extern / native Win32 call in it's Dispose().

IDisposable is used as a mechanism to free unmanaged resources (Which file handles are)

风吹雨成花 2024-07-25 16:27:26

我不得不把矛头指向垃圾收集器。 如果你受垃圾收集的摆布,把它留在身边并不是真正的问题。

这家伙也有类似的投诉...并且他找到了使用 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;
    }

...

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

I would have to point my finger at the Garbage Collector. Leaving it around is not really the issue if you are at the mercy of Garbage Collection.

This guy had a similar complaint... and he found a workaround of using a FileStream object rather than loading directly from the file.

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;
    }

...

It seems like a complete hack...

长亭外,古道边 2024-07-25 16:27:26

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

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

As mentioned above the Microsoft work around causes a GDI+ error after several images have been loaded. The VB solution for me as mentioned above by Steven is

picTemp.Image = Image.FromStream(New System.IO.MemoryStream(My.Computer.FileSystem.ReadAllBytes(strFl)))
叹梦 2024-07-25 16:27:26

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

在我的例子中,解决方案是在处理完每个图像后立即调用“.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()

I just encountered the same problem, where I was trying to merge multiple, single-page TIFF files into one multipart TIFF image. I needed to use Image.Save() and 'Image.SaveAdd()`: https://msdn.microsoft.com/en-us/library/windows/desktop/ms533839%28v=vs.85%29.aspx

The solution in my case was to call ".Dispose()" for each of the images, as soon as I was done with them:

' 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()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文