C# OutOfMemoryException:内存不足-System.Drawing.Graphics.FromImage
使用System.Drawing.Graphics.FromImage(在Windows 2012 server上使用最新版本的.NET软件)时出现内存不足异常,仅适用于极少数特定的图像文件。大多数情况下,代码工作正常 上述问题的典型答案表明某些资源没有被释放C# OutOfMemoryException:内存不足-System.Drawing.Graphics.FromImage,c#,.net,graphics,system.drawing,C#,.net,Graphics,System.drawing,使用System.Drawing.Graphics.FromImage(在Windows 2012 server上使用最新版本的.NET软件)时出现内存不足异常,仅适用于极少数特定的图像文件。大多数情况下,代码工作正常 上述问题的典型答案表明某些资源没有被释放 请在回答之前考虑如下:-< /强> 此特定图像的大小为34KB,是一个.JPG图像。服务器处于空闲状态,内存超过32GB 如果我看一下 此jpg文件使用windows资源管理器,通过右键单击文件,windows显示:96 dpi和3
<强>请在回答之前考虑如下:-< /强>
- 此特定图像的大小为34KB,是一个.JPG图像。服务器处于空闲状态,内存超过32GB
- 如果我看一下
此jpg文件使用windows资源管理器,通过右键单击文件,windows显示:96 dpi和32位深度李>
- 但是,如果我使用任何图形程序(如photoshop)打开此jpg文件,文件属性将显示为:72 dpi和24位深度李>
- 因此,我认为文件头属性之间有一个错误匹配 说出文件实际包含的内容李>
- 此外,如果我打开jpg 使用图形程序保存文件,只需重新保存,无需更改 任何内容,windows资源管理器中的文件属性现在匹配/读取正确 (72 dpi和24位深度);而文件由 System.Drawing.Graphics正确,没有引发异常
- 但是,如果我使用任何图形程序(如photoshop)打开此jpg文件,文件属性将显示为:72 dpi和24位深度李>
谢谢 虽然我不是JPEG文件格式的专家,但我对这一主题做了一些研究,以下是我的发现,可以帮助您解决您的问题 请注意,由于缺少示例文件来检查和说明它与.Net/GDI+JPEG/JFIF解码器期望的不同之处,此答案将假定而不是具体指出问题的根源 JPEG/JFIF格式 首先,您可能想了解一下JPEG/JFIF格式本身。毕竟,您刚刚遇到了一个.Net/GDI+无法加载/解析的文件。由于我没有您遇到问题的文件,我建议您将其加载到所选的十六进制编辑器中。。。它能够基于模板/代码/解析器突出显示文件 我使用了来自Sweetscape在线模板库的和。 010编辑器提供30天免费试用 您要特别查找的是坏JPEG中的SOFn标识符和数据 在SOFn数据中,我可以看到我的图像是Y(154)像素高,X(640)像素宽,使用3个分量,每个分量的精度为8位,使其成为每个像素24位。
JPEG/JFIF格式是许多不同实现/格式的巨大混合。显然,在奇怪的JPEG格式出现之前很久就存在的任何库中,都找不到该格式的所有变体。这是GDI+库所拥有的 在您的情况下,我怀疑您在JPEG文件中遇到了CMYK颜色配置文件 Net实现 您说过您使用了System.Drawing.Graphics.FromImage,因此我假设您的代码如下所示:
Graphics.FromImage(Image.FromFile("nope.jpg"));
Graphics.FromImage(Image.FromFile("nope.jpg", true));
Graphics.FromImage(Image.FromStream(nopeJpegStream));
从这些调用中,当本机gdiplus.dll调用
- GdipGetImageGraphicsContext
- GdipLoadImageFromFile
- GdipLoadImageFromFileICM(或其各自的*流变体)或
- GdipImageForceValidation
在任何情况下,这很可能不是.Net的问题,而是GDI+(gdiplus.dll)的问题,Microsoft没有为其提供源代码。这也意味着无法使用.Net包装器控制映像的加载方式,也无法检查其失败的原因。(尽管我仍然怀疑您的JPEG是用CMYK保存的) 不幸的是,当您在GDI+land中前进时,您将发现更多这些奇怪的异常/错误。因为该库几乎被弃用,取而代之的是Windows演示框架(WPF)和Windows映像组件。(WIC) 我自己的测试 由于你从未提供过关于这个主题的图片或任何其他细节,我试图重现你的问题。这本身就是一项任务,Image.FromFile(GdipLoadImageFromFile)将在许多不同的文件格式上失败。至少它不在乎文件扩展名是什么,幸好Photoshop做到了这一点 根据你的信息,我最终复制了一个.jpg文件,它在Photoshop中加载良好,显示DPI为96,位深度为32。当然,如果我对JPEG格式有更多的了解,我可能会马上找到解决方案 在010编辑器中显示这个文件(我必须在Photoshop中将其设置为CMYK颜色空间)给了我以下SOFn数据:Y(154)像素高,X(640)像素宽,使用4组件,每个组件的精度为8位,每个像素32位 我怀疑你会在你的“坏”文件上看到同样的情况。
是的,Image.FromFile现在抛出OutOfMemoryException 可能的解决方案
- 使用外部库加载图像文件。(我留给你做的练习,但A.K.A似乎是个不错的选择)
- 使用可以将图像从一种格式转换为另一种格式的命令行工具(在出现此异常时调用)。或者从JPEG转换为JPEG,就像在这种情况下一样。(同样,ImageMagick的“转换”命令行工具似乎是一个不错的选择)
- <
public static Image ImageFromFileWpf(string filename) { /* Load the image into an encoder using the Presentation Framework. * This is done by adding a frame (which in laymans terms is a layer) to a class derived BitmapEncoder. * Only TIFF, Gif and JPEG XR supports multiple frames. * Since we are going to convert our image to a GDI+ resource we won't support this as GDI+ doesn't (really) support it either. * If you want/need support for layers/animated Gif files, create a similar method to this one that takes a BitmapFrame as an argument and then... * 1. Instanciate the appropriate BitmapDecoder. * 2. Iterate over the BitmapDecoders frames, feeding them to the new method. * 3. Store the returned images in a collection of images. * * Finally, i opted to use a PngBitmapEncoder here which supports image transparency. */ var bitmapEncoder = new PngBitmapEncoder(); bitmapEncoder.Frames.Add(BitmapFrame.Create(new Uri(filename))); // Use a memorystream as a handover from one file format to another. using (var memoryStream = new MemoryStream()) { bitmapEncoder.Save(memoryStream); /* We MUST create a copy of our image from stream, MSDN specifically states that the stream must remain * open throughout the lifetime of the image. * We cannot instanciate the Image class, so we instanciate a Bitmap from our temporary image instead. * Bitmaps are derived from Image anyways, so this is perfectly fine. */ var tempImage = Image.FromStream(memoryStream); return new Bitmap(tempImage); } }
foreach (var encoder in ImageCodecInfo.GetImageEncoders()) { if (encoder.MimeType == "image/jpeg") image.Save(filename, encoder, new EncoderParameters { Param = new [] { new EncoderParameter(Encoder.Quality, 100L) }}); }
Image bitmap = Bitmap.FromFile(filename, false); Graphics graphics = null; try { graphics = Graphics.FromImage(bitmap); } catch (OutOfMemoryException oome) { // Well, this looks like a buggy image. // Try using alternate method ImageMagick.MagickImage image = new ImageMagick.MagickImage(filename); image.Resize(image.Width, image.Height); image.Quality = 90; image.CompressionMethod = ImageMagick.CompressionMethod.JPEG; graphics = Graphics.FromImage(image.ToBitmap()); }