【问题标题】:How to find reason for Generic GDI+ error when saving an image?保存图像时如何查找通用 GDI+ 错误的原因?
【发布时间】:2013-03-12 08:23:40
【问题描述】:

有一个代码在加载和存储图像时可以使用很长时间,我发现我有一个图像破坏了这个代码:

const string i1Path = @"c:\my\i1.jpg";
const string i2Path = @"c:\my\i2.jpg";

var i = Image.FromFile(i1Path);
i.Save(i2Path, ImageFormat.Jpeg);

例外是:

System.Runtime.InteropServices.ExternalException 发生

GDI+ 中出现一般错误。

在 System.Drawing.Image.Save(字符串文件名,ImageCodecInfo 编码器,EncoderParameters 编码器参数)
在 System.Drawing.Image.Save(字符串文件名,ImageFormat 格式)
在...

据我所知,这张图片并没有什么特别之处。它的大小约为 250 像素,可以在例如Windows 图像查看器或 Paint.NET:

(由于上图,上传到 Stack Overflow 后就不再报错了,我放了the original image here

我发现在调用Save method 时,目标图像文件被创建为零字节。

我真的不知道是什么导致了错误。

我的问题:

  • 您能想到任何会阻碍 .NET 保存图像的特殊情况吗?
  • 是否有任何方法(除了恐慌)来缩小此类错误的范围?

【问题讨论】:

  • MSDN 说从 Stream 加载的 Bitmap 要求 Stream 保持存在,只要 Bitmap 存在。
  • 与您的问题无关:但是如果路径不存在,则会发生相同的异常(我花了一段时间才发现我有错字)。可能会帮助别人。

标签: c# .net image gdi+ system.drawing


【解决方案1】:

只需以管理员身份使用 Visual Studio 或以管理员身份运行代码创建的应用程序,它应该可以顺利运行。 这是用户访问权限问题。 我遇到了同样的问题,并通过以管理员身份运行 Visual Studio 来解决它。

【讨论】:

    【解决方案2】:

    就我而言,我不小心删除了存储图像的目录。

    【讨论】:

      【解决方案3】:

      关键信息:

      // Using System.Drawing.Imaging:
      new Bitmap(image).Save(memoryStream, ImageFormat.Jpeg);
      

      必须将图像投射到位图以保存它。

      使用:

      // Using System.Drawing.Imaging:
      image.Save(memoryStream, ImageFormat.Jpeg);
      

      会抛出错误:

      保存图像时出现通用 GDI+ 错误

      【讨论】:

        【解决方案4】:

        我发现了这个问题,因为我也遇到了类似的错误,并且文件实际上是用零长度创建的(如果您没有看到任何文件,请首先检查写入文件夹的权限作为其他答案建议)。虽然我的代码略有不同(我使用stream 从内存中读取图像,而不是从文件中读取),但我认为我的回答可能对任何面临类似问题的人有所帮助。

        这可能看起来违反直觉,但在完成图像之前,您无法真正释放内存流。

        不工作

        Image patternImage;
        
        using (var ms = new MemoryStream(patternBytes)) {
            patternImage = new Bitmap(ms);
        }
        
        patternImage.Save(patternFile, ImageFormat.Jpeg);
        

        在完成图像处理之前不要释放流。

        作品

        using (var ms = new MemoryStream(patternBytes)) {
            patternImage = new Bitmap(ms);
            patternImage.Save(patternFile, ImageFormat.Jpeg);
        }
        

        什么是误导:

        • 错误信息并没有真正告诉你任何事情
        • 您可以看到图像属性,例如宽度和高度,但不能 保存它

        【讨论】:

        • 我遇到了类似的问题,这个解决方案也对我有用。谢谢。
        • 文档中是否提到了读取图像并在保存之前将其转换为位图?
        【解决方案5】:

        原因可能是图片延迟加载,尝试保存时加载过程尚未完成。

        按照this blog post 中所说的(假设您在问题中链接的图片是德国人)提供了一个可能的解决方案。 this SO question's 接受的答案也表明这是由于您尝试保存的图像文件被锁定。

        编辑
        对于 Ulysses Alves,来自链接的博客条目:如果您使用 Image.FromFile() 加载图像,它将保持锁定状态,直到它被处理掉。这可以防止调用Save()

        pictureBox1.Image = Image.FromFile("C:\\test\\test1.jpg");
        pictureBox1.Image.Save("C:\\test\\test2.jpg");
        

        以上代码抛出错误。

        要使其工作,您需要复制图像。以下代码有效:

        pictureBox1.Image = Image.FromFile("C:\\test\\test1.jpg");
        Image copy = pictureBox1.Image;
        copy.Save("C:\\test\\test2.jpg")
        

        【讨论】:

        • 我真的不明白为什么这个答案被否决了......也许有人可以解释一下?
        • 由于反对的选民没有发表评论,我们可能永远不会知道。 :)
        • 我在链接的帖子中找不到与此主题相关的任何内容。您能否在答案中提供一个代码示例?
        • 我的猜测是 Image copy = pictureBox1.Image;不是创建副本,而只是创建一个新的引用。
        【解决方案6】:

        在程序中打开

        const string i1Path = @"c:\my\i1.jpg";
        
        const string i2Path = @"c:\my\i2.jpg";
        
        var i = Image.FromFile(i1Path);
        
        i.Save(i2Path, ImageFormat.Jpeg);
        
        i.Dispose();
        

        【讨论】:

          【解决方案7】:

          我的解决方案是在保存文件之前编写临时内容 (File.WriteAllText)

          代码如下:

          var i = Image.FromFile(i1Path);
          File.WriteAllText(i2Path, "empty");  // <---- magic goes here
          i.Save(i2Path, ImageFormat.Jpeg);
          

          请尝试告诉我

          【讨论】:

          • 如果您没有创建该文件的权限,我可以看到当您遇到异常时/在哪里会发生变化。 :) :) 但如果这解决了 OP 中的其他问题,那就太奇怪了。 bitmap.Freeze(); bitmap.Save(...);var other = new Bitmap(bitmap); other.Save(...); 这两者都确保正在保存的位图在调用 Save 之前未绑定到流。
          【解决方案8】:

          首先确保所需文件夹具有读/写权限。更改权限为我解决了这个问题。

          【讨论】:

          • 这就像 90% 的情况 :-)
          【解决方案9】:

          解决办法来了,你必须dispose image 对象来释放服务器上的内存。 尝试使用using 语句。确保服务器上的目标目录也存在。

          【讨论】:

          • 这听起来对我来说是一个真的不好的答案。
          • “确保服务器上的目标目录存在”是我的问题
          • @UweKeim - 临时 IDisposable 对象上的 using 只是确保在安全的情况下尽快为它调用 GC。
          • “确保服务器上的目标目录存在”也是我的问题
          • “确保服务器上的目标目录存在”也是我的问题。很容易被忽视。
          【解决方案10】:

          虽然我在保存图像时仍然没有找出导致错误的确切原因,但我找到了一个解决方法:

          const string i1Path = @"c:\my\i1.jpg";
          const string i2Path = @"c:\my\i2.jpg";
          
          var i = Image.FromFile(i1Path);
          
          var i2 = new Bitmap(i);
          i2.Save(i2Path, ImageFormat.Jpeg);
          

          即通过将图像内部复制到Bitmap 实例中并保存此图像而不是原始图像,错误消失了。

          我假设通过复制它,导致原始 Save 调用失败的错误部分被删除和/或规范化,从而使保存操作能够成功。

          有趣的是,如此存储的图像在磁盘上的文件 (16 kB) 比其原始源 (26 kB) 小。

          【讨论】:

          • 这对我也有用。由于导致“内存不足异常”,我不得不在之后强制进行垃圾收集。
          • 也为我工作 - 不知道为什么。只有 jpg 图像有问题(png 等很好)
          • 我在使用 .bmp、.jpg 和 .png 时遇到了这个问题,但我没有加载文件,而是在内存中创建它并尝试保存它。 CAUSE: 原始位图与它加载的流相关联(FileStreamMemoryStream)。克隆的位图不绑定到流,因为它复制了像素字节。它可能在保存原始位图之前也对Freeze 起作用。
          • 我第二次遇到这个错误,我第一次使用与你相同的解决方案。但第二次它不起作用
          • 我遇到了画质快速下降的问题,我该如何解决这个问题
          猜你喜欢
          • 1970-01-01
          • 2011-06-21
          • 1970-01-01
          • 2017-08-18
          • 1970-01-01
          • 1970-01-01
          • 2013-09-26
          • 1970-01-01
          相关资源
          最近更新 更多