当我在IIS7中将PNG保存到MemoryStream时,发生了GDI+通用错误。

4

在保存png到内存流时,我遇到了错误。在我的ASP.NET开发服务器上一切正常,但是当我在IIS7下运行网站时,就会生成错误。同时,当我尝试保存jpg格式时,一切都正常。在IIS中,我已将.NET受信任级别设置为完全。但它仍然无法正常工作。我需要你的帮助。

private static Stream DrawBarChart(IEnumerable<Tuple<string, ulong, double>> data){
using (MemoryStream stream = new MemoryStream())
{
    var canvasWidth = PageSize.A4.Width;
    var canvasHeight = PageSize.A4.Height;

    using (
        System.Drawing.Image bitmap = new Bitmap((int) canvasWidth,
                                                 (int) canvasHeight))
    {
        using (var graphics = Graphics.FromImage(bitmap))
        {
            var penBlack1 = new Pen(Brushes.Black, 1);

            graphics.DrawLine(penBlack1, 0, 0, canvasWidth, 0);
            graphics.DrawLine(penBlack1, 0, 0, 0, canvasHeight);

            graphics.Save();
        }
        bitmap.Save(stream, ImageFormat.Png);

    stream.Flush();
    stream.Position = 0;

    return stream;
    }
}

}


可能与GDI+有关(其中Graphics仅是其封装),因为它在服务中不受支持?来源:“不支持在Windows服务中使用GDI+函数和类。试图从Windows服务中使用这些函数和类可能会产生意外问题,例如降低的服务性能和运行时异常或错误。” - CodeCaster
有没有其他生成图片的替代方法可用? - Alexander
Graphics.Save并不是你想象中的那样。请Google一下。 - usr
1个回答

3

你的代码中至少存在两个完全独立的问题导致它不能正常运行。

问题 #1:GDI+错误

这是一个经常在生态系统中出现的问题。我曾经遇到过它,我成功地解决了它,但现在我不记得具体是怎样解决的了。我尝试重复你的问题,但我未能失败。

我建议你检查这个主题:http://forums.asp.net/t/624305.aspx/1。这里一些人已经成功地克服了这个问题:

  • 确保在继续其他绘画之前处理掉所有的Bitmap实例
  • (有趣的一点):在每个位图渲染后进行GC.Collect()

这并不是我想要回答你的主要原因。看起来你已经做了所有我通常做的事情,以确保我不会陷入这种情况(安全性、信任等)。此外,你对自己的清理工作比必要的多(稍微多了一点,当你阅读我的答案时会发现)。

我发现你的代码还有第二个问题,你可能还没有意识到。在我自己的VS环境中,通过简单解决这个问题,我成功地渲染了位图(在IIS 7和Express和ASP开发服务器中都可以)。

很可能通过在应用程序代码中重新组织一下,你就能够解决问题 #1。因此,请查看我对问题 #2 的答案。

问题 #2:流不能被同时处理并返回

你不能创建并处理掉流,并将它们返回,如下所示:

public static Stream SomeMethod() {
  using (MemoryStream stream = new MemoryStream()) {
    // write something to the stream
    return stream;
  }
}

我真的不理解ASP.NET开发服务器中那段代码是如何工作的。我要指出的问题是,无论您将代码作为服务运行还是在交互式用户空间中运行,您的代码都会始终引发ObjectDisposedException:

Cannot access a closed stream

谁关闭了流?using语句的终止。

问题#2的可能解决方案

此特定问题的快速解决方案(可能会使用比预期更多的内存)是使您的方法返回一个byte[]而不是一个流。

public static byte[] SomeMethod() {
  using (MemoryStream stream = new MemoryStream()) {

    // ... create bitmap and render things ...

    // write something to the stream
    bitmap.Save(stream, ImageFormat.Png);

    return stream.ToArray();
  }
}

如果允许我对您的应用程序需求进行假设,我认为另一种解决方案可能会更好:

如果您希望通过ASP.NET通用处理程序将这些生成的图表图像返回到Web浏览器中的<img>标签,并且您是通过将当前Web响应的OutputStream传递给绘图方法来实现此目的,而不是从中获取结果byte[](或Stream),则可以这样做:

在HTML中:

<img src="path/Foo.ashx" alt="chart" ... etc ... />

在应用程序中:

public class Foo : IHttpHandler {
  public void ProcessRequest(HttpContext context) {

   context.Response.ContentType = "application/octet-stream";
   Stream alreadyExistingStream = context.Response.OutputStream;

   Etc.SomeMethod(stream);       
  }
}

public class Etc {

public static void SomeMethod(Stream stream) {
  // There used to be a using here that would create a stream
    // simply because the parameter name **stream** is the same as the former local var's name
    // the instructions that do the drawing of things 
    // + saving of the resulting Bitmap **to** the stream
    // keep on compiling without any problems

    // draw things to a bitmap
    // write the bitmap to the stream
    bitmap.Save(stream, ImageFormat.Png);

    // returning stuff is not needed anymore
  // This used to be the time and place where the stream would be disposed
  // and it's resources given back to the community
}

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接