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个回答

0

尝试这段代码:

static void Main(string[] args)
{
    byte[] data = null;
    string fullPath = @"c:\testimage.jpg";

    using (MemoryStream ms = new MemoryStream())
    using (Bitmap tmp = (Bitmap)Bitmap.FromFile(fullPath))
    using (Bitmap bm = new Bitmap(tmp))
    {
        bm.SetResolution(96, 96);
        using (EncoderParameters eps = new EncoderParameters(1))
        {   
            eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
            bm.Save(ms, GetEncoderInfo("image/jpeg"), eps);
        }

        data = ms.ToArray();
    }

    File.WriteAllBytes(fullPath, data);
}

private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
        ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();

        for (int j = 0; j < encoders.Length; ++j)
        {
            if (String.Equals(encoders[j].MimeType, mimeType, StringComparison.InvariantCultureIgnoreCase))
                return encoders[j];
        }
    return null;
}

0
public static byte[] SetImageToByte(Image img)
    {
        ImageConverter converter = new ImageConverter();
        return (byte[])converter.ConvertTo(img, typeof(byte[]));
    }
public static Bitmap SetByteToImage(byte[] blob)
    {
        MemoryStream mStream = new MemoryStream();
        byte[] pData = blob;
        mStream.Write(pData, 0, Convert.ToInt32(pData.Length));
        Bitmap bm = new Bitmap(mStream, false);
        mStream.Dispose();
        return bm;
    }

欢迎来到StackOverflow!请考虑添加一些解释文本以配合代码。一个好的开始:为什么要这样做而不是另一种方式? - Ivan Rubinson

0

我使用了ImageProcessor来调整图像大小,有一天我遇到了“GDI+中发生了通用错误”的异常。

经过一番搜索后,我尝试了回收应用程序池,然后它就奏效了。所以我在这里记录下来,希望能够帮到大家;)

祝好


0

当我尝试将图像保存到路径

C:\Program Files (x86)\some_directory

并且.exe未以管理员身份运行时,这个问题也出现在我身上。我希望这可以帮助遇到同样问题的人。


0
一个奇怪的解决方案竟然让我的代码正常工作了。在画图软件中打开图片,并以相同格式(.jpg)保存为新文件。现在尝试使用这个新文件,它就能正常工作了。这明显说明了原始文件可能以某种方式损坏了。只有在你的代码已经修复了其他所有错误的情况下,这个方法才有效。

0
今天在服务器上,当相同的代码在本地和 DEV 服务器上运行良好但在生产环境中出现错误。重启服务器解决了这个问题。

0

我在尝试在WPF应用程序中进行简单的图像编辑时遇到了这个错误。

将Image元素的Source设置为位图会阻止文件保存。 即使将Source设置为null,似乎也无法释放该文件。

现在我从不使用图像作为Image元素的Source,这样我就可以在编辑后覆盖它!

编辑

在听说CacheOption属性(感谢@Nyerguds)之后,我找到了解决方案: 因此,我必须在设置CacheOption BitmapCacheOption.OnLoad之后设置Uri,而不是使用Bitmap构造函数。(下面的Image1是Wpf Image元素)

与其使用

Image1.Source = new BitmapImage(new Uri(filepath));

使用:

var image = new BitmapImage();
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(filepath);
image.EndInit();
Image1.Source = image;

请看这个:WPF 图像缓存


1
WPF 图像具有特定的参数 BitmapCacheOption.OnLoad,可以将它们与加载源断开连接。 - Nyerguds
谢谢@Nyerguds,直到你的评论我才没有问对问题。 - mkb

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