如何让.Net保存此图像?

我有这个jpeg ,在picasa,photoshop,网页浏览器等打开很好,但在.Net它只是拒绝工作。

Image image = Image.FromFile(@"myimage.jpg"); image.Save(@"myimage2.jpg"); // ExternalException - A generic error occurred in GDI+. 

有没有办法在.Net中恢复它,所以我可以使用它(我需要调整它),而不是在源头修复问题?

完整的exception细节:

来源:System.Drawing 
 type:System.Runtime.InteropServices.ExternalException 
消息:GDI +中发生一般错误。 
堆栈跟踪:    
在System.Drawing.Image.Save(String filename,ImageCodecInfo encoder,EncoderParameters encoderParams)
   在System.Drawing.Image.Save(String filename,ImageFormat格式)
   在System.Drawing.Image.Save(String filename)
   在C:\ Users \ sam \ Desktop \ S中的ConsoleApplication20.Program.Main(String [] args)
 ource \ ConsoleApplication20 \ ConsoleApplication20 \ Program.cs:第16行

此问题可在Windows 7上重现。

它似乎在Windows XP和Vista上运行良好,但不适用于Windows 7。

我能够在Microsoft Connect上找到此问题 。 它与您的问题不同但看起来非常相似 – 尝试重新保存JPEG文件时发生ExternalException 。 目前有16个代表,包括一些评论,该问题仍然存在于最终版本中。

因此它看起来不是.NET Framework中的错误,而是Windows 7中的错误 – 特别是GdipSaveImageToStream API中的错误。

正如其他人所提到的,解决方法是强制转换为另一种格式。 这就是Marc和Darin的答案所做的。 JPEG文件中显然有一些“额外”信息触发了Win7中的错误。 当您转换为不同的格式或进行位图复制时,该信息(EXIF可能?)被消除。

这似乎有效:

  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的乐趣myimage2

关于这一点的一个好处是你可以同时resize(你的实际意图):

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

尝试明确指定格式:

 using (Image image = Image.FromFile(@"test.jpg")) { image.Save(@"myimage2.gif", ImageFormat.Gif); } 

所有ImageFormat.PngImageFormat.BmpImageFormat.Gif可以正常工作。 ImageFormat.Jpeg抛出exception。

原始图像采用JFIF格式,因为它以FF D8 FF E0字节开头。

这是.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不喜欢特定图像的内容(并且有各种失败案例的样本应该有人想要接受挑战)但是上面的方法有效,这很好。

尝试使用WPF的BitmapSource而不是WinForm的Image,它支持更多的像素,图像和文件格式。

我昨天在32位XP上尝试过并且无法再现这个问题。 今天我尝试使用64位Win7并完全解决了你所描述的错误,这对我的调试非常有用。

我调查了标题,JPEG是一个标准的JPEG,但带有EXIF标题。 从我读到的内容来看,EXIF标头可能已损坏并且某些程序将忽略它并不罕见。 在.NET中,它允许读取和(甚至可以允许在某些时候写入)但不允许写入。 你可以在这里读更多关于它的内容。
http://blogs.msdn.com/calvin_hsia/archive/2005/07/24/442873.aspx

删除它的一种方法是克隆像Marc建议的图像,这将创建没有EXIF标题的新图像,因此这解释了文件大小实际上更小的原因。 有一种以编程方式删除EXIF头的方法,有些已在StackOverflow中建议如下: 使用.NET从JPEG中删除EXIF数据的简单方法

读取字节标记并跳过流的建议问题将无法正常工作,因为我们正在处理损坏的EXIF标头。 我尝试使用RemovePropertyItem是一个选项,但它仍然不起作用,我的猜测是因为有被破坏的属性项被引用(当我检查JPEG标头有6个属性,.NET只加载4)。 他们是其他图书馆,如exiv2net,可以探索,但我怀疑结果将是类似的。

简而言之,答案将是按照Marc的建议克隆图像。 这可能不是解决方案,而是对问题的洞察力。

尝试检查您的权限。 无法保存可能是由于没有正确的写入/修改权限而导致的,并且可能会生成此错误。

当你有的时候会发生这种错误

 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) 

您需要处理原始文件,然后使用resize的克隆保存它,如Marc的答案。 我想我会告诉你为什么会这样。