每周源代码50 – 关于ASP.NET中“GDI+遇到了一个通用错误”和生成图片时的问题的一点调查

[原文发表地址]The Weekly Source Code 50 - A little on "A generic error occurred in GDI+" and trouble generating images on with ASP.NET

[原文发表时间] 2010-02-18 08:27 AM

最近,我的一些代码在VS开发环境下面运行的很好,当迁移到IIS下面时,遇到了一个看上去很小的图片出错提示,里面说“GDI+遇到了一个通用错误”,好吧,如果你遇到了一个通用错误,那说明问题的原因是不确定的。

我的小程序用一个本地文件存储了地图,当用户输入信息之后进行一些运算然后在地图上画一个X,最后把动态生成的图片返回。

在服务器端处理图像有三种方法:用nativeAPI(Windows自带的)和Interop,这种方法只能在本地,换句话说是程序得到全部权限的时候才能用;用System.Drawing,这种方式在服务器端“不支持”,或使用WPF,而且在微软官方文档中不建议这么做。我还在寻找他们这么规定的原因,不过清楚地是,我在负载很高的网站上使用System.Drawing是没问题的。我在非托管资源中花了很多心思,因此从来没遇到什么问题,我在闲谈时听说有人用GDI+(System.Drawing)时遇到了麻烦,然后就用WPF替代,而且没什么问题。正如上面所说,没有确定的结论,所以测试你所使用的方法,甚至可以使用ASP.NET 上的CodePlex控件来实现图像处理。

好吧,这篇文章不会解答所有会遇到“GDI+遇到了一个通用错误” 的原因,但是可以解答我遇到的情形。在我的例子里面(我觉得大部分人都是这种情况)我把处理过的图片保存成了PNG格式。

首先,给大家看一下5年前的一段代码,将两个图片合并成一个新的图片并保存。

public class SomeCheckImageHandler : IHttpHandler
{ //some stuff snipped
public SomeCheckImageHandler(){}
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "image/jpg";
//some stuff snipped
GetCheckImageRequest req = new GetCheckImageRequest();
//some stuff snipped, get the params from the QueryString
GetCheckImageResponse res = banking.GetCheckImage(req);

//some stuff snipped
if (res.ImageBack != null)
{
            //merge them into one image
using(MemoryStream m = new MemoryStream(res.BackImageBytes))
using(Image backImage = System.Drawing.Image.FromStream(m))
using(MemoryStream m2 = new MemoryStream(res.BrontImageBytes))
using(Image frontImage = System.Drawing.Image.FromStream(m2))
using(Bitmap compositeImage = new Bitmap(frontImage.Width,frontImage.Height+backImage.Height))
using(Graphics compositeGraphics = Graphics.FromImage(compositeImage))
{
compositeGraphics.CompositingMode = CompositingMode.SourceCopy;
compositeGraphics.DrawImageUnscaled(frontImage,0,0);
compositeGraphics.DrawImageUnscaled(backImage,0,frontImage.Height);
compositeImage.Save(context.Response.OutputStream, ImageFormat.Jpeg);
}
}
else //just show the front, we've got no back
{
using(MemoryStream m = new MemoryStream(frontImageBytes))
using(Image image = System.Drawing.Image.FromStream(m))
{
image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
}
}
}
}

这段代码生成了一个JPEG的图片,而且现在还运行的很好。现在我写的这段代码会创建一个PNG文件,当你创建一个PNG图片时,你需要一个可查找的字节流。下面这个小示例采用了Phil和我写的BaseHttpHandler

请注意示例中的高亮部分。(当你直接浏览这篇日志时可以看到高亮,如果你用RSS ,那就不行了)

关键在于创建PNG的过程:如果没有另外的中间MemoryStream作中转的话,你没法建立一个PNG文件,因为需要可查找的字节流。不能直接用image.Save() 来把Response.OutputStream直接保存成PNG文件。

namespace FooFoo
{
public class MapHandler : BaseHttpHandler
public override void HandleRequest(HttpContext context)
{
string filename = context.Server.MapPath(".") + @"\images\newmap2000.jpg";

using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
using (System.Drawing.Image image = System.Drawing.Image.FromStream(fs))
{
using (Graphics g = Graphics.FromImage(image))
{
BrickFinderDrawing drawer = new BrickFinderDrawing(...somedata...);
drawer.DrawBrick(g);
drawer.DrawName(g);

using (MemoryStream stream = new MemoryStream())
{
image.Save(stream, ImageFormat.Png);
stream.WriteTo(context.Response.OutputStream);
}
}
}
}
}
public override bool RequiresAuthentication
{
get { return false;
}
}
public override string ContentMimeType
{ get { return "image/png";
}
}
public override bool ValidateParameters(HttpContext context)
{
return true;
}
}
}

当我再次遇到类似黄色的错误提示时,希望我能记起这些。很高兴有一个博客可以记录这些。

相关链接