我有this JPEG image,在Picasa,Photoshop,网络浏览器等方面打开正常,但在.NET中它只是拒绝工作。
Image image = Image.FromFile(@"myimage.jpg");
image.Save(@"myimage2.jpg");
// ExternalException - A generic error occurred in GDI+.
有没有办法在.NET中恢复它,所以我可以使用它(我需要调整它),而不是在源头修复问题?
完整的异常详情:
source: System.Drawing type: System.Runtime.InteropServices.ExternalException message: A generic error occurred in GDI+. stack trace: at System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams) at System.Drawing.Image.Save(String filename, ImageFormat format) at System.Drawing.Image.Save(String filename) at ConsoleApplication20.Program.Main(String[] args) in C:\Users\sam\Desktop\S ource\ConsoleApplication20\ConsoleApplication20\Program.cs:line 16
此问题在Windows 7上可重现。
答案 0 :(得分:12)
这似乎有效:
using (Image image = Image.FromFile(@"c:\dump\myimage.jpg"))
using (Image clone = new Bitmap(image))
{
clone.Save(@"c:\dump\myimage2.jpg", ImageFormat.Jpeg);
}
image
实际上是Bitmap
,所以它应该是相似的。奇怪的是myimage2
小了5k - jpeg的乐趣;-p
这是一件好事,你可以同时调整大小(你的实际意图):
using (Image image = Image.FromFile(@"c:\dump\myimage.jpg"))
using (Image clone = new Bitmap(image,
new Size(image.Size.Width / 2, image.Size.Height / 2)))
{
clone.Save(@"c:\dump\myimage2.jpg", ImageFormat.Jpeg);
}
答案 1 :(得分:4)
尝试明确指定格式:
using (Image image = Image.FromFile(@"test.jpg"))
{
image.Save(@"myimage2.gif", ImageFormat.Gif);
}
所有ImageFormat.Png
,ImageFormat.Bmp
和ImageFormat.Gif
都可以正常使用。 ImageFormat.Jpeg
会抛出异常。
原始图片采用JFIF
格式,因为它以FF D8 FF E0
字节开头。
答案 2 :(得分:4)
这是.NET框架本身的一个长期存在的错误,我不希望它很快就会被修复。有几个相关的错误,我也遇到了抛出'GDI +中发生的一般错误',包括如果你访问一些JPEG的PropertyItem集合(检查EXIF代码)然后从那一点上你将无法保存图像。此外,没有押韵或原因,一些JPEG,如你在这里的那些,根本不会保存。请注意,只是保存为不同的格式并不总是有效,尽管在这种情况下也是如此。
我在我的应用程序中使用的解决方法是消耗来自整个网络的图片(因此看到的不仅仅是这些类型问题的公平份额)是捕获ExternalException并将图像复制到新的Bitmap正如之前的答案中的一个,然而简单地将其保存为新的JPEG会降低质量,因此我使用类似下面的代码来保持高质量:
namespace ImageConversionTest
{
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using System.Globalization;
class Program
{
static void Main( string[] args )
{
using( Image im = Image.FromFile( @"C:\20128X.jpg" ) )
{
string saveAs = @"C:\output.jpg";
EncoderParameters encoderParams = null;
ImageCodecInfo codec = GetEncoderInfo( "image/jpeg" );
if( codec != null )
{
int quality = 100; // highest quality
EncoderParameter qualityParam = new EncoderParameter(
System.Drawing.Imaging.Encoder.Quality, quality );
encoderParams = new EncoderParameters( 1 );
encoderParams.Param[0] = qualityParam;
}
try
{
if( encoderParams != null )
{
im.Save( saveAs, codec, encoderParams );
}
else
{
im.Save( saveAs, ImageFormat.Jpeg );
}
}
catch( ExternalException )
{
// copy and save separately
using( Image temp = new Bitmap( im ) )
{
if( encoderParams != null )
{
temp.Save( saveAs, codec, encoderParams );
}
else
{
temp.Save( saveAs, ImageFormat.Jpeg );
}
}
}
}
}
private static ImageCodecInfo GetEncoderInfo( string mimeType )
{
// Get image codecs for all image formats
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
// Find the correct image codec
foreach( ImageCodecInfo codec in codecs )
{
if( string.Compare( codec.MimeType, mimeType, true, CultureInfo.InvariantCulture ) == 0 )
{
return codec;
}
}
return null;
}
}
}
请注意,您将丢失EXIF信息,但我暂时不会这样做(即使保存源图像失败,您通常仍然可以读取EXIF代码)。我早就放弃了试图弄清楚.NET不喜欢特定图像的东西(并且有各种失败案例的样本应该有人想要接受挑战)但是上面的方法有效,这很好
答案 3 :(得分:3)
它似乎在Windows XP和Vista上运行良好,但不适用于Windows 7.
我找到了this issue on Microsoft Connect。它与您的问题不完全相同,但看起来非常相似 - 尝试重新保存JPEG文件时出现ExternalException
。目前有16个代表,包括一些评论,表明该问题仍然存在于最终版本中。
因此它看起来不是.NET Framework中的错误,而是Windows 7中的错误 - 特别是GdipSaveImageToStream
API中的错误。
其他人提到的解决方法是强制转换为其他格式。这就是Marc和Darin的答案所做的。 JPEG文件中显然有一些“额外”信息触发了Win7中的错误。当您转换为其他格式或制作位图副本时,该信息(EXIF可能?)将被删除。
答案 4 :(得分:2)
尝试使用WPF的BitmapSource而不是WinForm的Image,它支持更多的像素,图像和文件格式。
答案 5 :(得分:2)
我昨天在32位Windows XP上试过,无法重现这个问题。今天我尝试使用64位Windows 7并完全解决了您所描述的错误,这对我的调试非常有用。
我调查了标题,JPEG是标准的JPEG,但带有EXIF标题。从我读到的内容来看,EXIF标头可能损坏并且一些程序会忽略它并不罕见。在.NET中,它允许阅读和(甚至可以允许在某些时候写作),但不能写。您可以在博客文章 GDI+ can’t handle some malformed JPG files 中阅读更多相关信息。
删除它的一种方法是克隆像Marc建议的图像,这将创建没有EXIF标题的新图像,因此这解释了文件大小实际上更小的原因。有一些以编程方式删除EXIF头的方法,Stack Overflow中已经提出了一些方法 比如 Simple way to remove EXIF data from a JPEG with .NET 。
由于我们正在处理损坏的EXIF标头,因此读取字节标记并跳过流的建议问题将无法正常工作。我尝试使用RemovePropertyItem是一个选项,但它仍然不起作用,我的猜测是因为有被破坏的属性项被引用(当我检查JPEG标题有六个属性,而.NET只加载四个)。它们是exiv2net之类的其他库,可以进行探索,但我怀疑结果是相似的。
简而言之,答案将是按照Marc的建议克隆图像。这可能不是解决方案,而是对问题的洞察力。
答案 6 :(得分:0)
尝试检查您的权限。无法保存是因为没有正确的写/修改权限,可能会产生此错误。
答案 7 :(得分:-1)
当你有
时会发生错误a. no permissions to write the file
b. you are trying to write an image that is locked
(often, by a UI control ie. picturebox)
您需要处理原始文件,然后使用调整大小的克隆保存原文,如Marc的答案。我想我会告诉你为什么会这样。