【发布时间】: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+