Image.Save(..)抛出GDI+异常,因为内存流已关闭。

123

我有一些二进制数据,想要把它保存为图像。当我尝试保存这个图像时,如果用于创建图片的内存流在保存之前关闭了,它就会抛出异常。我之所以这样做是因为我正在动态创建图像,因此需要使用内存流。

以下是代码:

[TestMethod]
public void TestMethod1()
{
    // Grab the binary data.
    byte[] data = File.ReadAllBytes("Chick.jpg");

    // Read in the data but do not close, before using the stream.
    Stream originalBinaryDataStream = new MemoryStream(data);
    Bitmap image = new Bitmap(originalBinaryDataStream);
    image.Save(@"c:\test.jpg");
    originalBinaryDataStream.Dispose();

    // Now lets use a nice dispose, etc...
    Bitmap2 image2;
    using (Stream originalBinaryDataStream2 = new MemoryStream(data))
    {
        image2 = new Bitmap(originalBinaryDataStream2);
    }

    image2.Save(@"C:\temp\pewpew.jpg"); // This throws the GDI+ exception.
}

有没有人有建议,我如何在流关闭的情况下保存图像?我不能依赖开发人员记住在保存图像后关闭流。实际上,开发人员甚至不知道使用内存流生成了图像(因为它是在其他代码的其他地方发生的)。

我真的很困惑:(


1
我在另一个问题中从@HansPassant收到了这个评论。每当编解码器在写入文件时出现问题时,您将收到此异常。添加一个好的调试语句是在Save()调用之前添加System.IO.File.WriteAllText(path, "test"),它验证了创建文件的基本能力。现在,您将获得一个良好的异常,告诉您做错了什么。 - Juan Carlos Oropeza
你应该在 using 块内部调用 image2.Save。我认为 originalBinaryDataStream2 会在 using 结束时自动释放,这可能会抛出异常。 - taynguyen
17个回答

191

由于这是一个MemoryStream,如果您不关闭流,什么都不会发生,但显然最好将任何可处置的内容处理一下。有关更多信息,请参见此问题

但是,您应该处置位图,这将为您关闭流。基本上,一旦将流提供给位图构造函数,它就拥有了该流,并且您不应该将其关闭。如该构造函数的文档所述:

您必须在位图的生命周期内保持流处于打开状态。

我找不到任何文档承诺在处置位图时关闭流,但您应该能够轻松验证。


4
太棒了!Jon的回复很好,讲得非常清楚(我错过了文档中有关流的部分)。双手竖起来!我会在尝试后回报的 :) - Pure.Krome
如果我们想要遵守规则CA2000,你对此有什么建议?(msdn.microsoft.com/en-us/library/ms182289.aspx) - Patrick Szalapski
1
@Patrick:这根本不适用 - 你已经转移了资源的所有权。最接近的方法可能是创建一个“NonClosingStream”包装器,它忽略Dispose调用。我想我在MiscUtil中可能有一个... - Jon Skeet
谢谢@Jon提供的信息。对我来说,出于某种奇怪的原因,在本地开发环境中即使使用dispose()也能正常工作,但在生产环境中却无法工作。 - Oxon

108

在GDI+中发生了一般性错误。 也可能是由于保存路径不正确导致的! 我花了半天时间才注意到这一点。 因此,请确保您已经仔细检查了要保存图像的路径。


5
很高兴我看到了这个信息,我的路径是 "C\Users\mason\Desktop\pic.png"。缺少冒号!如果没有注意到这一点,我可能会永远寻找不到问题所在。 - mason
5
不正确也意味着你想要保存图像的文件夹不存在。 - Roemer

14

或许值得一提的是,如果C:\Temp目录不存在,即使您的流依然存在,它也会抛出这个异常。


+1 这个异常似乎在各种情况下都会发生。无效路径是我今天遇到的一个。 - Kirk Broadhurst

4

复制位图。您必须在位图的生命周期内保持流处于打开状态。

绘制图像时:System.Runtime.InteropServices.ExternalException:GDI 发生了一般性错误

    public static Image ToImage(this byte[] bytes)
    {
        using (var stream = new MemoryStream(bytes))
        using (var image = Image.FromStream(stream, false, true))
        {
            return new Bitmap(image);
        }
    }

    [Test]
    public void ShouldCreateImageThatCanBeSavedWithoutOpenStream()
    {
        var imageBytes = File.ReadAllBytes("bitmap.bmp");

        var image = imageBytes.ToImage();

        image.Save("output.bmp");
    }

1
这并不完全正确;在您的ToImage()代码中,本地变量“image”将正确地具有原始文件的.RawFormat(jpeg或png等),而ToImage()的返回值将意外地具有.RawFormat MemoryBmp。 - Patrick Szalapski
不确定 RawFormat 有多大作用。如果您想使用它,请在途中从对象中检索它,但通常情况下,保存为实际想要的类型。 - Nyerguds

4

我曾经也遇到过相同的问题,但实际上原因是该应用程序没有权限在 C 盘上保存文件。当我改成 "D:\.." 时,图片就被保存了。


3

您可以尝试创建位图的另一个副本:

using (var memoryStream = new MemoryStream())
{
    // write to memory stream here

    memoryStream.Position = 0;
    using (var bitmap = new Bitmap(memoryStream))
    {
        var bitmap2 = new Bitmap(bitmap);
        return bitmap2;
    }
}

2

当我试图从Citrix访问时,发生了这个错误。服务器上的图像文件夹设置为C:\,而我没有权限。一旦将图像文件夹移动到共享驱动器中,错误消息就消失了。


1

我遇到了这个错误,因为我正在执行的自动化测试尝试将快照存储到一个不存在的文件夹中。创建文件夹后,错误得到解决。


1
在GDI+中发生了一般性错误。这可能是由于图像存储路径问题引起的。我遇到了这个错误,因为我的存储路径太长了,我通过先将图像存储在较短的路径中,然后使用长路径处理技术将其移动到正确的位置来修复它。

0
对我来说,下面的代码在保存到MemoryStream的那一行崩溃并显示A generic error occurred in GDI+。该代码正在Web服务器上运行,我通过停止和启动运行站点的应用程序池来解决了这个问题。
肯定是GDI+内部出现了一些错误。
    private static string GetThumbnailImageAsBase64String(string path)
    {
        if (path == null || !File.Exists(path))
        {
            var log = ContainerResolver.Container.GetInstance<ILog>();
            log.Info($"No file was found at path: {path}");
            return null;
        }

        var width = LibraryItemFileSettings.Instance.ThumbnailImageWidth;

        using (var image = Image.FromFile(path))
        {
            using (var thumbnail = image.GetThumbnailImage(width, width * image.Height / image.Width, null, IntPtr.Zero))
            {
                using (var memoryStream = new MemoryStream())
                {
                    thumbnail.Save(memoryStream, ImageFormat.Png); // <= crash here 
                    var bytes = new byte[memoryStream.Length];
                    memoryStream.Position = 0;
                    memoryStream.Read(bytes, 0, bytes.Length);
                    return Convert.ToBase64String(bytes, 0, bytes.Length);
                }
            }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接