Bitmap.Save在几个小时后停止工作

时间:2014-07-31 15:54:19

标签: c# asp.net .net bitmap gdi+

过去几个月,我们开始面临一个问题,即 System.Drawing.Bitmap.Save(string filename) 方法在服务器运行几个小时后停止工作。它开始失败的小时数与服务器负载成正比。

该调用所涉及的代码已经运行了好几年,它从用户获取并获取图像,调整大小并将其保存到磁盘。该应用程序是一个开发.Net Framework 3.5的ASP.Net应用程序。

当对Bitmap.Save的调用失败时,会引发臭名昭着的异常:

  

错误消息:错误genéricoenGDI +。
堆栈跟踪:         en System.Drawing.Image.Save(String filename,ImageCodecInfo encoder,EncoderParameters encoderParams)
        en System.Drawing.Image.Save(String filename,ImageFormat format)
        en System.Drawing.Image.Save(String filename)

我一直在阅读很多关于该异常的帖子,我完全清楚在许多不同的情况下会引发这个异常。这是一个通用的异常,由于许多不同的原因(即,当应用程序没有写入文件夹的permisison,当代码试图将图像保存在它正在读取的同一文件中时,等等),但是没有这些情况就是我们的情况。

当我们认为该方法失败时,我们刚刚发布了我们的应用程序的新版本,所以我们最初认为错误可能在我们的代码中(尽管该部分没有被更改,我们认为我们已经改变了其他的东西,干扰并产生错误),但我们调查的越多,我们就越不了解错误发生的时间。

同样重要的是,在出现错误的同时,我们还使用最新的Windows更新更新了我们的服务器,在那里我还看到了两个可疑的:

KB2929755 http://support.microsoft.com/kb/2929755/en-us
在Windows应用程序中加载某些图像资源时内存不足

KB2957503 http://support.microsoft.com/kb/2957503/en-us
MS14-036:Windows 7,Windows Server 2008 R2,Windows Server 2008,Windows Vista和Windows Server 2003安全更新说明:2014年6月10日
这会更新服务器上的GDIplus.dll以避免一些安全问题。

我的感觉是服务器上的某些东西没有被释放,就像某些代码泄漏处理程序或内存一样,并且在某些时候服务器耗尽它们并且无法再保存Bitmap。出现错误时,从应用程序的任何部分对Bitmap.Save的任何调用都会引发该异常。重新启动IIS会解决问题直到下一次,但是,如果我们只重新启动应用程序池,情况就不是这样了。

如果它有任何帮助,我们使用HiQPdf库(版本6.8.0.0)将一些HTML文档转换为PDF。此工具为每次转换创建一个新进程,并在完成时将其终止,并假设以适当的方式释放所有使用的资源。

有人遇到任何类似的问题吗?

是否有人对所述的Windows更新有任何疑问?

我忘了还提到我们尝试使用WPF API而不是GDI +来调整图像大小但是我们遇到了同样的问题,几个小时后服务器开始引发错误(虽然这次消息是ExcepcióndeHRESULT:0x88982F8A ),所以我们将它还原为原始代码。

任何帮助将不胜感激!

修改

实际上,执行图像调整大小和保存的代码如下。所有变量都在使用块内,但是对于将图像映射到Bitmap,我假设它仍然被处理掉了:

using (Image originalLogo = Image.FromStream(fuLogo.PostedFile.InputStream))
{
    using (Image resizedLogo = ImageUtil.ResizeImage(originalLogo, maxWidth, maxHeight))
    {
        ((System.Drawing.Bitmap)resizedLogo).Save(newFilePath);
    }
}

对Bitmap的转换是否需要创建一个中间变量来强制它的处置,或者不需要它?

using (Image originalLogo = Image.FromStream(fuLogo.PostedFile.InputStream))
{
    using (Image resizedLogo = ImageUtil.ResizeImage(originalLogo, maxWidth, maxHeight))
    {
        using (Bitmap bitmap=((System.Drawing.Bitmap)resizedLogo))
        {
            bitmap.Save(newFilePath);
        }
    }
}

谢谢, 安瑞科

修改

我粘贴在ResizeImage方法的代码下面:

    public static System.Drawing.Image ResizeImage(System.Drawing.Image originalImage, int maxWidth, int maxHeight)
    {
        int imgWidth = originalImage.Width;
        int imgHeight = originalImage.Height;

        if (originalImage.Height<maxHeight && originalImage.Width<maxWidth) return originalImage;

        double widhtRatio = ((double)maxWidth) / ((double)imgWidth);
        double heightRatio = ((double)maxHeight) / ((double)imgHeight);

        int targetWidth, targetHeight;
        if (widhtRatio < heightRatio)
        {
            targetWidth = (int)(imgWidth * widhtRatio);
            targetHeight = (int)(imgHeight * widhtRatio);
        }
        else
        {
            targetWidth = (int)(imgWidth * heightRatio);
            targetHeight = (int)(imgHeight * heightRatio);
        }


         //Not all pixel formats are supported, therefore, using the 
         //originalImage pixel format raises an exception if the pixel format is not supported.
         //By not specifying it, the Bitmap constructor will use one compatible with the FromImage method.
         //For more info see: http://forums.asp.net/p/1195630/2066153.aspx             
         //   Image resizedImage = new Bitmap(targetWidth, targetHeight, originalImage.PixelFormat); 

        System.Drawing.Image resizedImage = new Bitmap(targetWidth, targetHeight);
        using (Graphics riGraphics = Graphics.FromImage(resizedImage))
        {
            riGraphics.CompositingQuality = CompositingQuality.HighQuality;
            riGraphics.SmoothingMode = SmoothingMode.HighQuality;
            Rectangle rectangle = new Rectangle(0, 0, targetWidth, targetHeight);
            riGraphics.DrawImage(originalImage, rectangle);
            riGraphics.Flush();
        }
        return resizedImage;
    }

1 个答案:

答案 0 :(得分:1)

对你来说,我在黑暗中有绝对的刺伤:

在您的应用程序代码中,位图对象是否包含在using中,还是在调用Save()后显式处理?我已经看到很难跟踪过去在处理IDisposible对象时因内存泄漏引起的异常,当它们被重复使用并且没有得到妥善处理时。这些情况似乎总是间歇性的,就像你所描述的那样,并且在实现解决方案之前就像调用Bitmap.Dispose()一样简单(或者在重新考虑因素或代码更新期间意外删除它之后将其放回去) )。

然而不太可能,因为听起来你的代码在应用Windows更新之前有效......我以为我会把它扔出去。