System.Drawing.Image FromStream不一致地返回内存不足异常

时间:2015-11-12 14:43:50

标签: c# image bytearray

我正在尝试改进我们的Web应用程序图像上传功能,该功能将图像作为字节数组存储到数据库中,然后稍后将其读出并将它们放入要显示的HTML图像标记中。

为了显示上传的所有图像,我们有一组单独的方法来检索缩略图,包括读出数据库,转换为内存流,然后使用它创建一个C#图像,后跟.GetThumbnail通过另一个内存流对象将其转换回字节数组之前的方法。

网格会加载所有数据(图像名称,描述,类别等),然后调用带有图像ID的单独URL来检索缩略图图像。此URL返回C#MVC ImageResult。当我单独调用此URL时,它会加载正确的缩略图而没有任何问题。但是,当我调用网格时,它可以很好地加载其他图像,然后在内存不足的情况下崩溃。如果我跳过这个,它也会继续加载其他图像。

起初我认为这可能是因为其中一个流打开但是所有内容都包含在使用Dispose()和Close()调用两个内存流(第一个将其转换为图像,第二个在finally块中将其转换回字节数组。

我完全没有想法,因为看起来字节数组是相同的,被调用的方法是相同的,但是在一个实例中它起作用而另一个实例不起作用。

我复制了违规代码,这是通过我们的Image对象将图像转换为缩略图的方法(作为第4个参数传入),一致地落在System.Drawing.Image.FromStream(ms)行上。< / p>

  private static void ConvertToImage(int size, bool fixWidth, bool fixHeight, Image img)
    {
        byte[] picbyte = img.Img;
        using (MemoryStream ms = new MemoryStream(picbyte))
        { 
            try
            {

                System.Drawing.Image image = System.Drawing.Image.FromStream(ms);
                int width = image.Width;
                int height = image.Height;

                if (fixWidth && !fixHeight)
                {
                    height = (int)Math.Round(((decimal)height / width) * size);
                    width = size;
                }

                if (fixHeight && !fixWidth)
                {
                    width = (int)Math.Round(((decimal)width / height) * size);
                    height = size;
                }

                if ((fixWidth && fixHeight) || (!fixWidth && !fixHeight))
                {
                    width = size;
                    height = size;
                }

                IntPtr ptr = Marshal.AllocHGlobal(sizeof(int));

                int ptrInt = 0;
                Marshal.WriteInt32(ptr, ptrInt);

                Marshal.FreeHGlobal(ptr);
                MemoryStream ms2 = new MemoryStream();
                using (image = image.GetThumbnailImage(width, height, delegate () { return false; }, ptr))
                { 
                    try
                    {
                        image.Save(ms2, System.Drawing.Imaging.ImageFormat.Png);
                        img.Img = ms2.ToArray();
                        img.MIMEType = "image/png";
                        image.Dispose();
                    }
                    finally
                    {
                        ms2.Close();
                        ms2.Dispose();
                    }
                }
            }
            finally //Ensure we close the stream if anything happens.
            {
                ms.Close();
                ms.Dispose();
            }
        }


    }

1 个答案:

答案 0 :(得分:3)

GDI(像ImageBitmap之类的东西是包装器),如果更好的命名是更好的命名,非犹豫,{OutOfMemoryExecption,那就很糟糕。{ 1}}。

在.NET中使用图像时, 必须 始终处理您的资源,您使用的对象通常是不占用大量内存的类但要坚持有限的非托管资源。因为如果你创建了很多垃圾收集器,它们不会对垃圾收集器造成很大的内存压力,你可以在GC运行之前轻松地用完GDI handles并收集它们。

在功能的顶部你可以

OutOfHandlesException

然后你做

System.Drawing.Image image = System.Drawing.Image.FromStream(ms);

这会导致您丢失对第一个 using (image = image.GetThumbnailImage(width, height, delegate () { return false; }, ptr)) 对象的引用而不进行处理。为缩略图使用不同的变量名称,并将第一个图像放在Image块中。

P.S。您的using.Close();来电是无关紧要的。将可处置对象放在.Dispose()块中执行这两个操作,您可以摆脱所有try-finally块并摆脱额外的using调用。另外,您的image.Dispose()不正确,the MSDN states您应该传递IntPtr.Zero而不是写入指针的值0。

这是一个快速更新的版本,包含所有修复程序。

ptr