【问题标题】:Bitmap.Save stops working after some hoursBitmap.Save 几个小时后停止工作
【发布时间】:2014-09-23 16:10:20
【问题描述】:

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

与该调用相关的这段代码多年来一直运行良好,它从用户那里获取图像、调整大小并将其保存到磁盘。该应用程序是 .Net Framework 3.5 开发的 ASP.Net 应用程序。

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

错误消息:GDI+ 中的一般错误。
堆栈跟踪: zh System.Drawing.Image.Save(字符串文件名,ImageCodecInfo 编码器,EncoderParameters 编码器参数)
zh System.Drawing.Image.Save(字符串文件名,ImageFormat 格式)
zh System.Drawing.Image.Save(字符串文件名)

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

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

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.Save 的任何调用都会引发该异常。重新启动 IIS 可以解决问题,直到下一次,但是,如果我们只重新启动应用程序池,则情况并非如此。

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

有人遇到过类似的问题吗?

有人对所描述的 Windows 更新有任何问题吗?

我还忘了提到我们尝试使用 WPF API 而不是 GDI+ 来调整图像大小,但我们遇到了同样的问题,几个小时后服务器开始出现错误(尽管这次消息是 Excepción de HRESULT: 0x88982F8A ),因此我们将其还原为原始代码。

任何帮助将不胜感激!

编辑:

实际上,执行图像大小调整和保存的代码如下。所有变量都在 using 块中,但对于将图像投射到位图,我认为无论如何都会被处理:

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

【问题讨论】:

  • 你能在 IIS 之外重现这个吗?例如:编写一个测试应用程序?然后,您也许可以在其上运行分析器并单步执行代码。该方法的源代码可用:referencesource.microsoft.com/#System.Drawing/commonui/System/…
  • 不是真的,我一直在开发一个独立的应用程序,它可以生成 PDF 并调用我们的应用程序 API 调整图像大小,看看我是否可以在本地重现错误,但到目前为止我还不能.
  • 该代码不会调整图像大小,ImageUtil.ResizeImage 会。你能显示那个代码吗?
  • 我会将位图保存到字节数组(使用内存流),然后将字节数组写入磁盘(File.WriteAlBytes)。这将为您提供有关该问题的更多信息。保存到内存或磁盘时可能会出现异常,如果写入磁盘时出现,您将获得更好的错误描述。

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


【解决方案1】:

我为你准备了绝对的暗中刺伤:

在您的应用程序代码中,位图对象是包裹在 using 中还是在调用 Save() 后显式处置?在过去处理 IDisposible 对象时,当它们被重复使用并且没有得到正确处理时,我发现很难跟踪内存泄漏引发的异常。这些情况似乎总是像您所描述的那样是断断续续的,并且在意识到解决方案就像调用 Bitmap.Dispose() 一样简单(或者在重构或代码更新期间将其意外删除后将其放回原处)之前进行了大量挖掘。

但不太可能,因为听起来您的代码在应用 Windows 更新之前就可以工作了……我想我会把它扔在那里。

【讨论】:

  • 是的。我实际上已经编辑了问题以显示执行图像大小调整和保存的代码。
  • 您的原始 using 语句对我来说看起来不错。是否可以看到 ImageUtil.ResizeImage() 方法?
  • 抱歉,我的回答迟了,我一直在休年假,并且不上班,但是团队仍在努力寻找解决此问题的方法,因此不胜感激。我用 ImageUtil.ResizeImage 的代码编辑了帖子。
  • 如果这能说明问题,当事情开始失败时,服务器可以很好地处理不需要调整大小的 JPEG 图像,但是当需要调整图像大小或使用 JPEG 的任何其他格式时,我们会得到GDI+ 异常。奇怪的是它可以正常工作几个小时,直到它开始失败。另一个有趣的事实是,在同一应用程序的同一服务器上的同一 IIS 中的另一个部署不会像最初的那样失败。不确定这是否有任何帮助,但我们的想法已经不多了。
猜你喜欢
  • 1970-01-01
  • 2018-12-19
  • 1970-01-01
  • 1970-01-01
  • 2012-12-20
  • 2018-02-20
  • 1970-01-01
  • 1970-01-01
  • 2021-11-22
相关资源
最近更新 更多