如何让.NET保存此图像?

时间:2010-02-26 08:30:52

标签: c# .net image

我有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上可重现。

8 个答案:

答案 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.PngImageFormat.BmpImageFormat.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的答案。我想我会告诉你为什么会这样。