每周源代码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;    
        }  
    }
}

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

相关链接


Comments (0)

Skip to main content