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

Joe*_*Joe 1 c# image bytearray

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

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

网格加载所有数据(图像名称、描述、类别等),然后使用图像 ID 调用单独的 URL 以检索缩略图图像。此 URL 返回 C# MVC ImageResult。当我自己调用这个 URL 时,它会毫无问题地加载正确的缩略图。但是,当我调用网格时,它会正常加载其他图像,然后出现内存不足异常。如果我跳过这个,它也会继续加载其他图像。

起初我认为这可能是由于让其中一个流保持打开状态,但所有内容都包含在 using with Dispose() 和 Close() 在两个内存流上调用(第一个将其转换为图像,第二个将其转换为图像)回到一个字节数组)在 finally 块中。

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

我复制了有问题的代码,它是通过我们的 Image 对象(作为第 4 个参数传入)将图像转换为缩略图的方法,始终落在 System.Drawing.Image.FromStream(ms) 行上。

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


    }
Run Code Online (Sandbox Code Playgroud)

Sco*_*ain 5

GDI(事类,如Image以及Bitmap用于包装)是不好扔OutOfMemoryExecption在一个更好的异常会是更好的命名,非exsitant, OutOfHandlesException

在 .NET 中使用图像时,您必须始终处理资源,您使用的对象通常是不占用大量托管内存但保留有限非托管资源的类。因为它们不会给垃圾收集器带来太大的内存压力,如果您创建大量垃圾收集器,您很容易在 GC 运行和收集它们之前耗尽GDI 句柄

在你的功能的顶部,你做

System.Drawing.Image image = System.Drawing.Image.FromStream(ms);
Run Code Online (Sandbox Code Playgroud)

然后你以后做

 using (image = image.GetThumbnailImage(width, height, delegate () { return false; }, ptr))
Run Code Online (Sandbox Code Playgroud)

这会导致您丢失对第一个Image对象的引用而不处理它。为您的缩略图使用不同的变量名称并将第一张图像放在一个using块中。


PS 你.Close();.Dispose()电话是unnessesary。将可处置的对象放在一个using块中执行这两个操作,您可以摆脱所有 try-finally 块并摆脱额外的image.Dispose()调用。此外,您的说法ptr不正确,MSDN 指出您应该传入而IntPtr.Zero不是将值 0 写入指针。

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

private static void ConvertToImage(int size, bool fixWidth, bool fixHeight, Image img)
{
    byte[] picbyte = img.Img;
    using (MemoryStream ms = new MemoryStream(picbyte))
    using (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;
        }

        using(MemoryStream ms2 = new MemoryStream())
        using (var thumnailImage = image.GetThumbnailImage(width, height, delegate () { return false; }, IntPtr.Zero))
        { 
            thumnailImage.Save(ms2, System.Drawing.Imaging.ImageFormat.Png);
            img.Img = ms2.ToArray();
            img.MIMEType = "image/png";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)