代码之家  ›  专栏  ›  技术社区  ›  Sam Saffron James Allen

如何让.NET保存此图像?

  •  9
  • Sam Saffron James Allen  · 技术社区  · 16 年前

    我有 this JPEG image 这在Picasa、Photoshop、Web浏览器等中打开得很好,但在.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
    

    这个问题在Windows7上是可复制的。

    8 回复  |  直到 7 年前
        1
  •  3
  •   Aaronaught    16 年前

    它在WindowsXP和Vista上似乎可以正常工作,但在Windows7上却不行。

    我能找到 this issue on Microsoft Connect . 这和你的问题不一样,但看起来 非常 类似-an ExternalException 尝试重新保存jpeg文件时发生。目前共有16份报告,其中包括一些评论,指出该问题仍然存在于最终版本中。

    因此,它看起来不是.NET框架中的bug,而是Windows7中的bug——特别是 GdipSaveImageToStream 应用程序编程接口。

    正如其他人提到的,解决方法是强制转换为另一种格式。这就是马克和达林的答案。显然,jpeg文件中有一些“额外的”信息正在触发win7中的错误。当您转换为其他格式或进行位图复制时,该信息(可能是exif?)被消除。

        2
  •  12
  •   Marc Gravell    16 年前

    这似乎有效:

        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 小5公里-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);
        }
    
        3
  •  4
  •   Darin Dimitrov    16 年前

    尝试显式指定格式:

    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 字节。

        4
  •  4
  •   Ruben    16 年前

    这是.NET框架本身的一个长期存在的错误,我不希望很快看到修复。我还遇到了几个相关的bug,它们抛出了“gdi+中发生的一般性错误”,包括如果您访问某些jpeg的propertyitem集合(检查exif代码),那么从那时起您将无法保存图像。而且,一些JPEG,比如你在这里的JPEG,因为没有押韵或原因,根本无法保存。请注意,保存为不同的格式也不总是有效,尽管在本例中确实如此。

    我在我的一个应用程序中使用的解决方法是捕获外部异常并将图像复制到一个新的位图中,就像以前的答案一样,只需将其保存在S一个新的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对特定图像不喜欢什么的想法(如果有人愿意接受挑战的话,我还准备了各种失败案例的样本),但是上面的方法是有效的,这很好。

        5
  •  2
  •   Danny Varod    16 年前

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

        6
  •  2
  •   Peter Mortensen Pieter Jan Bonestroo    7 年前

    我昨天在32位WindowsXP上尝试过,但无法重现问题。今天我在64位的Windows7上进行了尝试,得到了您所描述的错误,这对我的调试很好。

    我研究了头文件,jpeg是标准的jpeg,但带有exif头文件。据我所读,exif头可能已损坏,一些程序将忽略它,这并不少见。在.NET中,它允许阅读(有时甚至允许写作),但不允许写作。你可以在博客上阅读更多关于它的信息。 GDI+ can’t handle some malformed JPG files .

    删除它的一种方法是像marc建议的那样克隆图像,这样就可以创建不带exif头的新图像,因此这就解释了文件大小实际上更小的原因。有一些方法可以通过编程方式删除exif头,在stackoverflow中有一些建议 喜欢 Simple way to remove EXIF data from a JPEG with .NET .

    读取字节标记和跳过流的建议问题不会像我们处理损坏的exif头那样一直有效。我尝试使用removepropertyitem是一个选项,但它仍然不起作用,我猜是因为有损坏的属性项被引用(当我检查jpeg头时,有六个属性,.net只加载四个)。它们是其他类似于exiv2net的库,可以进行探索,但我怀疑结果会类似。

    简而言之,答案是按照马克的建议复制图像。这也许不是解决方案,而是对问题的洞察。

        7
  •  0
  •   Pbirkoff    16 年前

    尝试检查您的权限。无法保存可能是因为没有正确的权限进行写入/修改,并可能生成此错误。

        8
  •  -1
  •   Steven Evers    16 年前

    当你有

    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)
    

    您将需要处理原始文件,然后用您调整大小的克隆文件保存在上面,如马克的答案所示。我只是想告诉你为什么会这样。