【问题标题】:Image.Save(..) throws a GDI+ exception because the memory stream is closedImage.Save(..) 抛出 GDI+ 异常,因为内存流已关闭
【发布时间】:2010-09-25 01:59:42
【问题描述】:

我有一些想要保存为图像的二进制数据。当我尝试保存图像时,如果用于创建图像的内存流在保存之前已关闭,则会引发异常。我这样做的原因是因为我正在动态创建图像,因此..我需要使用内存流。

这是代码:

[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.
}

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

我真的很困惑:(

【问题讨论】:

  • 我在另一个question 中收到了来自@HansPassant 的评论。每当编解码器无法写入文件时,您都会收到此异常。在 Save() 调用之前添加一个很好的调试语句是 System.IO.File.WriteAllText(path, "test"),它验证了创建文件的基本能力。你现在会得到一个很好的异常,告诉你你做错了什么。
  • 您应该将 image2.Save 保存在 using 块内。我认为originalBinaryDataStream2 是在使用结束时自动处理的。那会抛出异常。

标签: c# image exception gdi+


【解决方案1】:

因为它是一个 MemoryStream,你真的不需要需要关闭流 - 如果你不这样做,不会发生任何不好的事情,尽管很明显,无论如何处理任何一次性的东西都是一个好习惯。 (有关更多信息,请参阅this question。)

但是,您应该处理位图 - 这将为您关闭流。基本上,一旦你给 Bitmap 构造函数一个流,它就“拥有”这个流,你不应该关闭它。正如the docs for that constructor 所说:

您必须保持流打开 位图的生命周期。

我找不到任何承诺在您处理位图时关闭流的文档,但您应该能够相当轻松地验证这一点。

【讨论】:

  • 太棒了!这是一个很好的答复乔恩。完美的感觉(我错过了文档中有关流的部分)。太棒了!当我试一试时,我会报告:)
  • 如果我们想遵守 CA2000 规则,有什么方法可以解决这个问题吗? (msdn.microsoft.com/en-us/library/ms182289.aspx)
  • @Patrick:这根本不适用——基本上,你已经转移了资源的所有权。您最接近的方法是创建一个忽略 Dispose 调用的“NonClosingStream”包装器。我想我可能在 MiscUtil 中有一个 - 不确定...
  • 感谢@Jon 的信息。对我来说,出于某种奇怪的原因,它甚至可以在本地开发环境中使用 dispose(),但在生产环境中不起作用。
【解决方案2】:

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

When drawing an image: System.Runtime.InteropServices.ExternalException: A generic error occurred in 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");
    }

【讨论】:

  • 这并不完全有效;在 ToImage() 中的代码中,本地“图像”将正确地具有原始文件(jpeg 或 png 等)的 .RawFormat,而 ToImage() 的返回值将意外地具有 .RawFormat MemoryBmp。
  • 不确定RawFormat 的重要性如何。如果你想使用它,从对象中的某个地方检索它,但一般来说,保存为你真正想要拥有的任何类型。
【解决方案3】:

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

【讨论】:

  • +1 这个异常似乎出现在各种场景中。无效路径是我今天遇到的一个。
【解决方案4】:

GDI+ 中出现一般错误。 也可能是不正确的保存路径! 我花了半天时间才注意到这一点。 因此,请确保您也仔细检查了保存图像的路径。

【讨论】:

  • 我很高兴看到这个,我的路径是C\Users\mason\Desktop\pic.png。缺少结肠!在我注意到这一点之前,我会花很长时间。
  • 不正确也意味着您要保存图像的文件夹不存在。
【解决方案5】:

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

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

【讨论】:

    【解决方案6】:

    GDI+ 中出现一般错误。它可能是由于图像存储路径问题而发生的,我收到此错误是因为我的存储路径太长,我通过首先将图像存储在最短路径中并使用长路径处理技术将其移动到正确位置来解决此问题。

    【讨论】:

      【解决方案7】:

      一个奇怪的解决方案使我的代码工作。 在paint中打开图像并将其另存为具有相同格式(.jpg)的新文件。现在尝试使用这个新文件,它可以工作。它清楚地向您解释了该文件可能以某种方式损坏。 仅当您的代码已修复所有其他错误时,这才有帮助

      【讨论】:

        【解决方案8】:

        我也遇到了同样的问题,但实际上原因是应用程序没有权限在 C 上保存文件。当我更改为“D:\..”时,图片已保存。

        【讨论】:

          【解决方案9】:

          我在尝试使用 Citrix 时出现此错误。图像文件夹在服务器中设置为 C:\,我没有权限。将图像文件夹移动到共享驱动器后,错误就消失了。

          【讨论】:

            【解决方案10】:

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

            【讨论】:

              【解决方案11】:

              当我尝试将图像保存到路径时,它也出现在我身边

              C:\Program Files (x86)\some_directory

              并且.exe 没有以管理员身份运行,我希望这也可以帮助遇到同样问题的人。

              【讨论】:

                【解决方案12】:

                对我来说,下面的代码在保存到 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);
                                }
                            }
                        }
                    }
                

                【讨论】:

                  【解决方案13】:

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

                  将图像元素的源设置为位图可防止文件保存。 即使设置 Source=null 似乎也不会释放文件。

                  现在我只是从不使用图像作为图像元素的来源,所以我可以在编辑后覆盖!

                  编辑

                  在听说 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 Image Caching

                  【讨论】:

                  • WPF 图像有一个特定的参数BitmapCacheOption.OnLoad 来断开它们与加载源的连接。
                  • 谢谢@Nyerguds,直到你的评论我没能提出正确的问题
                  【解决方案14】:

                  试试这个代码:

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

                  【讨论】:

                    【解决方案15】:

                    我使用图像处理器调整图像大小,有一天我收到“GDI+ 中发生一般错误”异常。

                    看了一会儿后,我尝试回收应用程序池,然后宾果游戏成功了。所以我在这里记下,希望对你有帮助;)

                    干杯

                    【讨论】:

                      【解决方案16】:

                      我今天在服务器上遇到此错误,当时相同的代码在本地和我们的 DEV 服务器上运行良好,但在 PRODUCTION 上却没有。重启服务器即可解决。

                      【讨论】:

                        【解决方案17】:
                        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!请考虑添加一些说明文本以配合代码。一个很好的起点:为什么这样做而不是另一种方式?
                        猜你喜欢
                        • 2012-09-10
                        • 2011-05-09
                        • 1970-01-01
                        • 1970-01-01
                        • 2014-05-18
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多