System.Drawing.Image.FromFile() 抛出了“内存不足”的异常。

51

我有一个图片上传器和裁剪器,它可以创建缩略图,但是有时候在下面这一行会出现内存不足的异常:

Dim bm As Bitmap = System.Drawing.Image.FromFile(imageFile)

出错的情况非常罕见,但我总是想知道可能是什么原因。imageFile变量只是指向图像路径的Server.MapPath。

我很好奇是否有人之前遇到过这个问题,以及他们是否有任何想法可能是什么原因导致的?也许是图像的大小吗?

如果需要,我可以发布代码和任何支持性信息,但很想听听大家对此的看法。


你正在尝试加载的图片非常大吗? - RCIX
4
您确定所讨论的文件是有效的图像文件吗?我也遇到了完全相同的情况,但该文件无法在任何其他程序中加载。 - Dan Byström
1
我是danby,感谢您的回复。这不是我的图片,所以我会让它被发送过来并进行检查。上传的人特别无能,所以我希望只是这个问题! - dooburt
1
好的,图片已经损坏了。问题解决了。谢谢大家。Danby,如果你添加你的答案,我会将其标记为正确的 ;) - dooburt
遇到了类似的问题。注意到当尝试读取损坏的文件时,System.Drawing.Image.FromFile会引发OutOfMemoryException异常。如果您创建一个0字节的图像文件,然后像您所做的那样尝试从文件中读取它,就可以重现这个问题。 - Sola Oderinde
显示剩余3条评论
13个回答

49

值得注意的是,OutOfMemoryException并不总是意味着内存耗尽——特别是在处理文件时。我相信如果由于某些原因而耗尽句柄,也会出现此问题。

你是否在使用完所有位图后进行了处理?这种情况是否会反复发生在单个图像上?


嗨Jon,感谢你快速回复。是的,我同时拥有图像和图形。Dim bmPhoto As New Bitmap(targetW, targetH, PixelFormat.Format24bppRgb) Dim grPhoto As Graphics = Graphics.FromImage(bmPhoto) bmPhoto.Dispose() bmPhoto = Nothing grPhoto.Dispose() grPhoto = Nothing至于可重复性,不,它非常随机 - 但是图像总是趋向于更大(但不超过700k〜)。 - dooburt
4
我猜测问题不在于文件大小,而在于内存中图像的大小——一个具有大量像素的极度压缩的图像可能会导致你出现问题... - Jon Skeet
2
嗨Jon,感谢你的评论。结果证明一直都是一张损坏的图片。叹气。一个无能的用户。不过还是谢谢你的回答 :) - dooburt
10
你是如何确定图像已损坏的?我在使用Image.FromFile时也遇到了OutOfMemoryException错误。奇怪的是,它只在一个服务器上抛出异常,而另一个则没有。我是否缺少某些必要的编码器?这是从Getty图片下载的4256x2832像素大小、24bpp和sRGB格式的JPEG图像。 - Mark Richman
1
嗨,马克,我尝试直接通过操作系统打开图像。它无法打开。Photoshop或任何其他图像软件也无法打开它。这导致了异常的抛出。 - dooburt

38
如果这不是一个损坏的图像文件,而实际上是通常存在的问题,即它会保留文件句柄,则解决方法是改用。
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
   using (Image original = Image.FromStream(fs))
   {
      ...

使用 Dispose() 方法、using() 语句或将位图的值设置为 null 都无法解决使用 Image.FromFile 时出现的问题。

因此,如果您的应用程序运行时间较长且需要打开大量文件,请考虑改用 Image.FromStream()


实际上,如果你通过非托管句柄绑定了一个文件流并处理它,那么句柄将立即关闭,不需要等待GC发生。毕竟,这就是IDisposable的全部意义所在。 - Lasse V. Karlsen
这还可以更进一步。http://blogs.msdn.com/b/omars/archive/2004/03/29/100941.aspx 仍然适用。 - user82646
1
在这种情况下,带有alpha通道的PDF怎么办? - Berker Yüceer
3
为什么调用Dispose()方法不会释放图像的文件句柄?这只是实现中的错误吗?为什么微软从未修复这个错误?显然,出于许多原因,使用流更好,但似乎微软应该已经解决了这个问题或彻底删除了该方法。 - crush

7
我今天在为一整个文件夹的图片创建缩略图时遇到了同样的问题。结果发现每次都会在同一点出现 "内存不足" 错误。当我查看待转换的图片文件夹时,发现问题出在thumbs.db上。我添加了一些代码,以确保只有图片文件才被转换,问题得到了解决。
我的代码基本上是:
For Each imageFile as FileInfo in fileList
If imageFile.Extension = ".jpg" Or imageFile.Extension = ".gif" Then
    ...proceed with the conversion
End If
Next

希望这能有所帮助。

是的,未处理的图像类型会被抛出为OOM。每次都让我措手不及。 - Nick Westgate

4

同时检查一下是否在其他地方打开了相同的文件。显然,当您两次打开一个文件(即使是使用File.Open())时,也会抛出OutOfMemoryException异常...


3

同时,您也可以在阅读模式下打开它(如果您想同时在两个地方使用它)。

 public Image OpenImage(string previewFile)
        {
            FileStream fs = new FileStream(previewFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            return Image.FromStream(fs);
        }

2
这种情况通常是由于图像文件损坏引起的。错误提示并不准确,因为这与内存无关。虽然我还没有解决编码问题,但使用try/catch/finally结构可以防止程序崩溃。"最初的回答"

1
今天我在尝试调整图像大小并裁剪时遇到了类似的问题,我使用了以下代码来调整图像大小。
private static Image resizeImage(Image imgToResize, Size size)
{
   int sourceWidth = imgToResize.Width;
   int sourceHeight = imgToResize.Height;

   float nPercent = 0;
   float nPercentW = 0;
   float nPercentH = 0;

   nPercentW = ((float)size.Width / (float)sourceWidth);
   nPercentH = ((float)size.Height / (float)sourceHeight);

   if (nPercentH < nPercentW)
      nPercent = nPercentH;
   else
      nPercent = nPercentW;

   int destWidth = (int)(sourceWidth * nPercent);
   int destHeight = (int)(sourceHeight * nPercent);

   Bitmap b = new Bitmap(destWidth, destHeight);
   Graphics g = Graphics.FromImage((Image)b);
   g.InterpolationMode = InterpolationMode.HighQualityBicubic;

   g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
   g.Dispose();

   return (Image)b;
}

然后这段代码用于裁剪...

private static Image cropImage(Image img, Rectangle cropArea)
{
   Bitmap bmpImage = new Bitmap(img);
   Bitmap bmpCrop = bmpImage.Clone(cropArea,
   bmpImage.PixelFormat);
   return (Image)(bmpCrop);
}

那么这就是我如何调用上面的代码...

Image img = Image.FromFile(@"C:\Users\****\Pictures\image.jpg");
img = ImageHandler.ResizeImage(img, new Size(400, 300));
img = ImageHandler.CropImage(img, new Rectangle(0, 25, 400, 250));
long quality = 90;

在裁剪部分我一直收到错误,但是调整大小的部分运行正常!

原来,在调整大小的过程中发生了错误并传递到裁剪函数。计算时,实际图像尺寸变成了399而不是我传入的400。

所以,当我将400作为裁剪参数传入时,它试图裁剪一个399像素宽度的图像,而这个图像的宽度应该是400像素,结果导致内存溢出错误!

以上大部分代码来源于http://www.switchonthecode.com/tutorials/csharp-tutorial-image-editing-saving-cropping-and-resizing


1
我曾经写过一个将TIFF转换为PDF的工具,也遇到了和你一样的问题。往往会在同一行出现“内存不足”的错误提示。
System.Drawing.Image.FromFile(imageFile)

然后我发现只有当文件扩展名为“.tiff”时才会出错,将其重命名为“.tif”扩展名后就可以正常工作了。


1
如果一张图片是图标,则需要进行不同的加载处理,就像下面的函数中所示:
public static Image loadImage(string imagePath)
    {
        Image loadedImage = null;
        if (!File.Exists(imagePath)) return loadedImage;
        try
        {
            FileInfo fileInfo = new FileInfo(imagePath);
            if (fileInfo.Extension.Equals(".jpg") || fileInfo.Extension.Equals(".jpeg") ||
               fileInfo.Extension.Equals(".bmp") || fileInfo.Extension.Equals(".png") ||
               fileInfo.Extension.Equals(".gif"))
            {
                loadedImage = Image.FromFile(imagePath);
            }
            else if (fileInfo.Extension.Equals(".ico"))
            {
                Bitmap aBitmap = Bitmap.FromHicon(new
                                           Icon(imagePath, new Size(200, 200)).Handle);
                loadedImage = ImageFuncs.ResizeImage(aBitmap, new Size(30, 30));
            }
        }
        catch (Exception eLocal)
        {
            MessageBox.Show(imagePath + " loading error: " + eLocal.Message);
        }
        return loadedImage;
    }

1

我之前遇到过同样的问题,在查看代码其他部分之前,我想确认一下是否可以用任何图像查看器打开该图像,并发现该图像已经损坏/损坏,尽管它是一个大小为1KB的.PNG文件。在相同位置添加了一个新图像,然后它就正常工作了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接