如何找到保存图像时出现通用 GDI+ 错误的原因?

46

我有一个能够长期加载和存储图像的代码,但我发现只有一张图片会导致代码出错:

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(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
at System.Drawing.Image.Save(String filename, ImageFormat format)
at ...

就我所看到的,这张图片没有任何特别之处。它大约有250像素大小,并且可以在Windows图像查看器或Paint.NET中打开:

enter image description here

(由于上面的图片上传到Stack Overflow后不再产生错误,因此我放了原始图片的链接)

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

我真的不知道是什么原因导致了这个错误。

我的问题:

  • 您能想到任何特殊的事情会阻止.NET保存图片吗?
  • 除了恐慌外,还有什么方法可以缩小这些错误的范围吗?

4
MSDN指出,从流中加载的位图要求该流在位图存在期间保持存在。 - Jesse Chisholm
3
与您的问题无关:但是如果路径不存在,会发生相同的异常(花了我一些时间才发现我打错了)。这可能会帮助其他人。 - Jürgen Steinblock
11个回答

59

虽然我仍未找出保存图像时发生错误的确切原因,但我找到了一个可行的解决方法:

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调用失败的错误部分被删除或规范化,从而使保存操作成功。

saved image i2.jpg

有趣的是,所存储的图像在磁盘上具有比其原始来源(26 kB)更小的文件大小(16 kB)。


1
这对我也起作用了。由于引起的“内存不足异常”,之后我不得不强制进行垃圾回收。 - user426364
4
对我也有用 - 不知道为什么。只有JPG图像出现了问题(PNG等都没问题)。 - Darren Wainwright
3
我曾经遇到过关于 .bmp、.jpg 和 .png 格式的问题,但并非加载文件,而是在内存中创建图像然后尝试保存。 原因: 原始位图与它所加载的流 (FileStreamMemoryStream) 相关联。复制的位图不与流相关联,因为它只是拷贝了像素字节。在保存之前冻结原始位图也可能起作用。 - Jesse Chisholm
我第二次遇到了这个错误,不知何故第一次使用了与您相同的解决方案,但第二次却无效。 - Abdelsalam Hamdi
我有一个问题,图片质量迅速下降,我该如何解决? - user13616016

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

2
那就像是90%的情况 :-) - Artur Kedzior
通常情况下,这几乎总是一个“无法写入”错误。因此,请确保文件名不包含斜杠或其他特殊字符。 - Cas Bloem

9
解决方案在这里,您必须处置图像对象以释放服务器上的内存。尝试使用using语句。确保目标目录在服务器上存在。

2
这对我来说听起来是一个非常糟糕的答案。 - Uwe Keim
3
我的问题是“确保服务器上的目标目录存在”。 - Michael Blake
2
@UweKeim - 在临时的 IDisposable 对象上使用 using 只是确保在安全的情况下尽快调用 GC。 - Jesse Chisholm
确保服务器上的目标文件夹已经存在,这也是我的问题。 - GWR
“确保服务器上的目标目录存在”也是我的问题。很容易忽略。 - daveD

6
可能的原因是图像懒加载并且在尝试保存时加载过程还未完成。根据这篇博客文章(假设您是通过您问题中提供的图片推断出您是德国人),可以提供一个可能的解决方案。此外,这个SO问题的接受的答案表明,这是由于您尝试保存的图像文件被锁定的原因。
编辑: 对于链接的博客条目中的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")

1
我真的不明白为什么这个答案被踩了...也许有人可以解释一下? - Thorsten Dittmar
2
由于反对者没有留下评论,我们可能永远不会知道。 :) - Jesse Chisholm
在链接的帖子中,我找不到任何与这个主题相关的内容。您能否在回答中提供一个代码示例? - Ulysses Alves
3
我的猜测是,Image copy = pictureBox1.Image; 这行代码并没有创建一个副本,而只是创建了一个新的引用。 - Frank Tzanabetis

4
我发现这个问题,因为我也遇到了类似的错误,并且文件实际上是用零长度创建的(如果您没有看到任何文件,请首先检查写入文件夹的权限,就像其他答案建议的那样)。虽然我的代码略有不同(我使用流来从内存中读取图像,而不是从文件中读取),但我认为我的答案可能对任何面临类似问题的人都有帮助。
它可能看起来有点违反直觉,但在完成图片操作之前,您不能真正释放内存流。
不起作用的代码:
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);
}

什么是误导的:

  • 错误消息不真正告诉你任何信息
  • 您可以查看图像属性,例如宽度和高度,但无法保存它

1
我遇到了类似的问题,这个解决方案对我也起作用了。谢谢。 - Sarvjeet Verma
文档中是否提到了在保存之前读取图像并将其转换为位图的操作? - Eakan Gopalakrishnan

3

我的解决方案是在保存文件之前,先创建临时内容并写入(使用File.WriteAllText函数)

以下是代码:

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

请尝试并告诉我


1
如果你没有创建该文件的权限,我可以看到在哪里/何时会发生异常。:) :) 但如果这解决不了其他OP中的问题,那就很奇怪了。bitmap.Freeze(); bitmap.Save(...);或者var other = new Bitmap(bitmap); other.Save(...);。这两个都确保在调用Save之前保存的位图未绑定到流。 - Jesse Chisholm

3

我的情况是我不小心删除了存储图像的目录。


1
关键信息:

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

必须将图像转换为位图才能保存。

使用:

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

会抛出错误:

保存图像时发生通用 GDI+ 错误


1

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


1
在我的情况下,我将validateImageData设置为false:
Image.FromStream(stream, validateImageData: false);

解决方案:

Image.FromStream(stream, validateImageData: true);

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