过去几个月,我们开始面临一个问题,即 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;
}
答案 0 :(得分:1)
对你来说,我在黑暗中有绝对的刺伤:
在您的应用程序代码中,位图对象是否包含在using
中,还是在调用Save()后显式处理?我已经看到很难跟踪过去在处理IDisposible对象时因内存泄漏引起的异常,当它们被重复使用并且没有得到妥善处理时。这些情况似乎总是间歇性的,就像你所描述的那样,并且在实现解决方案之前就像调用Bitmap.Dispose()
一样简单(或者在重新考虑因素或代码更新期间意外删除它之后将其放回去) )。
然而不太可能,因为听起来你的代码在应用Windows更新之前有效......我以为我会把它扔出去。