简体   繁体   English

如何让.NET保存此图像?

[英]How can I get .NET to save this image?

I have this JPEG image , that opens fine in Picasa, Photoshop, web browser, etc., but in .NET it just refuses to work. 我有这个JPEG图像 ,在Picasa,Photoshop,网络浏览器等中打开很好,但在.NET中它只是拒绝工作。

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

Is there a way to recover it in .NET so I can work with it (I need to resize it), without fixing the problem at the source? 有没有办法在.NET中恢复它,所以我可以使用它(我需要调整它),而不是在源头修复问题?

Full exception details: 完整的异常细节:

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

This issue is reproducible on Windows 7. 此问题可在Windows 7上重现。

This seems to work: 这似乎有效:

    using (Image image = Image.FromFile(@"c:\dump\myimage.jpg"))
    using (Image clone = new Bitmap(image))
    {
        clone.Save(@"c:\dump\myimage2.jpg", ImageFormat.Jpeg);
    }

image is actually a Bitmap anyway, so it should be similar. 无论如何, image实际上是一个Bitmap ,所以它应该是类似的。 Oddly myimage2 is 5k smaller - the joys of jpeg ;-p 奇怪的是myimage2小了5k - jpeg的乐趣myimage2

A nice thing about this is that you can resize at the same time (your actual intent): 关于这一点的一个好处是你可以同时调整大小(你的实际意图):

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

Try explicitly specifying the format: 尝试明确指定格式:

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

All ImageFormat.Png , ImageFormat.Bmp and ImageFormat.Gif work fine. 所有ImageFormat.PngImageFormat.BmpImageFormat.Gif可以正常工作。 ImageFormat.Jpeg throws an exception. ImageFormat.Jpeg抛出异常。

The original image is in the JFIF format as it starts with FF D8 FF E0 bytes. 原始图像采用JFIF格式,因为它以FF D8 FF E0字节开头。

This is a long-standing bug in the .NET framework itself that I don't expect to see fixed any time soon. 这是.NET框架本身的一个长期存在的错误,我不希望它很快就会被修复。 There are several related bugs I've also come across that throw the 'generic error occurred in GDI+' including if you access the PropertyItem collection for some JPEGs (to examine the EXIF codes) then from that point on you won't be able to save the image. 有几个相关的错误,我也遇到了抛出'GDI +中发生的一般错误',包括如果你访问一些JPEG的PropertyItem集合(检查EXIF代码)然后从那一点上你将无法保存图像。 Also, for no rhyme or reason some JPEGs, like the one you have here, simply won't save at all. 此外,没有押韵或原因,一些JPEG,如你在这里的那些,根本不会保存。 Note that just saving as a different format won't always work either, although it does in this case. 请注意,只是保存为不同的格式并不总是有效,尽管在这种情况下也是如此。

The workaround I've employed in an app of mine that consumes pictures from all over the web (and therefore sees more than its fair share of these types of problems) is to catch the ExternalException and copy the image into a new Bitmap as in one of the previous answers, however simply saving this as a new JPEG will drop the quality rather a lot so I use code much like that below to keep the quality high: 我在我的应用程序中使用的解决方法是消耗来自整个网络的图片(因此看到的不仅仅是这些类型问题的公平份额)是捕获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;
        }
    }
}

Note that you'll lose the EXIF information but I'll leave that for now (you'll generally still be able to read the EXIF codes, even when saving the source image fails). 请注意,您将丢失EXIF信息,但我现在将保留此信息(即使保存源图像失败,您通常仍然可以读取EXIF代码)。 I have long since given up attempting to figure out what it is that .NET doesn't like about particular images (and have samples of various failure cases should anybody ever want to take up the challenge) but the approach above works, which is nice. 我早就放弃了试图弄清楚.NET不喜欢特定图像的东西(并且有各种失败案例的样本应该有人想要接受挑战)但是上面的方法有效,这很好。

It seems to work fine on Windows XP and Vista, but not Windows 7. 它似乎在Windows XP和Vista上运行良好,但不适用于Windows 7。

I was able to find this issue on Microsoft Connect . 我能够在Microsoft Connect上找到此问题 It's not identical to your issue but it looks very similar - an ExternalException occurring when trying to resave a JPEG file. 它与您的问题不同但看起来非常相似 - 尝试重新保存JPEG文件时发生ExternalException Currently at 16 repros including some comments that the issue still exists in the final release. 目前有16个代表,包括一些评论,该问题仍然存在于最终版本中。

So it looks not to be a bug in the .NET Framework, but a bug in Windows 7 - specifically, a bug in the GdipSaveImageToStream API. 因此它看起来不是.NET Framework中的错误,而是Windows 7中的错误 - 特别是GdipSaveImageToStream API中的错误。

The workaround, as others have mentioned, is to force a conversion to another format. 正如其他人所提到的,解决方法是强制转换为另一种格式。 That's what both Marc and Darin's answers are doing. 这就是Marc和Darin的答案所做的。 There is obviously some "extra" information in the JPEG file that is triggering the bug in Win7. JPEG文件中显然有一些“额外”信息触发了Win7中的错误。 When you convert to a different format or make a bitmap copy, that information (EXIF maybe?) is eliminated. 当您转换为不同的格式或进行位图复制时,该信息(EXIF可能?)被消除。

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

I tried yesterday on 32-bit Windows XP and can't reproduce the problem. 我昨天在32位Windows XP上试过,无法重现这个问题。 Today I tried on 64-bit Windows 7 and get exactly the error you described, which is great for my debugging. 今天我尝试使用64位Windows 7并完全解决了您所描述的错误,这对我的调试非常有用。

I investigated the header and the JPEG is a standard JPEG, but with an EXIF header. 我调查了标题,JPEG是一个标准的JPEG,但有一个EXIF标题。 From what I read, it is not uncommon that the EXIF header maybe corrupt and some programs will just ignore it. 从我读到的内容来看,EXIF标头可能损坏并且一些程序会忽略它并不罕见。 In .NET it allows reading and (may even allow writing at some point), but not writing. 在.NET中,它允许阅读和(甚至可以允许在某些时候写作),但不能写。 You can read more about it in blog post GDI+ can't handle some malformed JPG files . 您可以在博客文章中阅读更多相关内容, GDI +无法处理一些格式错误的JPG文件

One way to remove it is to clone the image like suggested by Marc, which will create the new image without an EXIF header, hence that explains why the file size is actually smaller. 删除它的一种方法是克隆像Marc建议的图像,这将创建没有EXIF标题的新图像,因此这解释了文件大小实际上更小的原因。 There are methods for removing an EXIF header programmatically, and some has been suggested in Stack Overflow like Simple way to remove EXIF data from a JPEG with .NET . 有一些以编程方式删除EXIF头的方法,并且Stack Overflow中已经提出了一些方法,比如使用.NET从JPEG中删除EXIF数据的简单方法

The suggested problem with reading the byte marker and skip the stream will not work well consistently as we are dealing with a corrupted EXIF header. 读取字节标记并跳过流的建议问题将无法正常工作,因为我们正在处理损坏的EXIF标头。 I tried using RemovePropertyItem is an option, but it still doesn't work either, and my guess is because there are corrupted property items that are being referred to (when I inspect the JPEG header there are six properties, and .NET only loads four). 我尝试使用RemovePropertyItem是一个选项,但它仍然不起作用,我的猜测是因为有被破坏的属性项被引用(当我检查JPEG标题有六个属性,而.NET只加载四个)。 They are other library like exiv2net which can be explored, but I suspect the outcome will be similar. 他们是其他图书馆,如exiv2net,可以探索,但我怀疑结果将是类似的。

In short, the answer will be to clone the image as suggested by Marc. 简而言之,答案将是按照Marc的建议克隆图像。 This is perhaps not the solution, but an insight into the problem. 这可能不是解决方案,而是对问题的洞察力。

Try checking your permissions. 尝试检查您的权限。 Not being able to save can be caused by not having the right permission to write/modify, and could generate this error. 无法保存可能是由于没有正确的写入/修改权限而导致的,并且可能会生成此错误。

That error occurrs when you either have 当你有的时候会发生这种错误

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)

You'll need to dispose the original, and then save over it with your resized clone as in Marc's answer. 您需要处理原始文件,然后使用调整大小的克隆保存原文,如Marc的答案。 I just figured I'd tell you why it's happening. 我想我会告诉你为什么会这样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用 asp.net webapi 将图像保存在数据库中 - How Can I save image in database usoing asp.net webapi 在Windows应用程序中,如何使用C#.Net 2.0从PictureBox中的网络摄像头保存实时图像 - In windows application how can i save a live image from webcam in picturebox using C#.Net 2.0 如何在 wwwroot For ASP.NET MVC 中保存输入图像? - How can i save an input image in the wwwroot For ASP.NET MVC? 如何使用 .NET Core 和 EF 将图像文件保存到 SQL 数据库 - How can I save Image file to SQL database with .NET Core and EF 如何下载和保存图像两次? - How can I download and save an image twice? 如何将映像保存到文件系统? - How can I save an Image to the File System? 如何将该图像保存为高清图像? - How can I save this image as HD as possible? 如何将图像保存到 Azure Blob 并使用特定字段命名并返回 Blob Url 并将其保存到 .net core 2.2 中的数据库 - How can i save an image to a Azure Blob and name it with specific fields and return the Blob Url and save it to database in .net core 2.2 如何获取进程窗口句柄intptr并将进程窗口保存到图像文件? - How can i get ap rocess window handle intptr and save the process window to image file? 如何在 ASP.NET Core 6 Web API 中以像素形式获取图像内容并从中创建图像? - How can I get image content as pixel and create image from it in ASP.NET Core 6 Web API?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM