【问题标题】:Alternatives to System.Drawing for use with ASP.NET?用于 ASP.NET 的 System.Drawing 的替代品?
【发布时间】:2010-12-04 10:51:25
【问题描述】:

在追踪奇怪的 GDI+ 错误几天后,我在MSDN 上偶然发现了这个小宝石:

System.Drawing 命名空间中的类不支持在 Windows 或 ASP.NET 服务中使用。尝试在其中一种应用程序类型中使用这些类可能会产生意想不到的问题,例如服务性能下降和运行时异常。

我不知道“ASP.NET 服务”在这种情况下是否意味着“Web 应用程序”,但“服务性能下降”似乎涵盖了“GDI+ 中发生一般错误”和“超出范围”的随机分类我的应用程序抛出的“内存”错误 - 读取和写入 JPEG 图像的间歇性、不可重现的错误 - 在许多情况下 - 实际上是由 System.Drawing.Imaging 首先创建的。

那么 - 如果 GDI+ 无法在 Web 应用程序中可靠地读取和写入 JPEG 文件,我应该改用什么?

我希望用户能够上传图像(需要 JPEG,也可以使用其他格式),可靠地重新采样,并在出现任何问题时显示有用的错误消息。有任何想法吗? WPF 中的 System.Media 命名空间值得考虑吗?

编辑: 是的,我知道 GDI+“大部分时间”都有效。这还不够好,因为当它失败时,它会以一种无法优雅地隔离或恢复的方式这样做。我对适合您的 GDI+ 代码示例不感兴趣:我正在寻找用于图像处理的替代库。

【问题讨论】:

标签: c# asp.net gdi+ system.drawing system.drawing.imaging


【解决方案1】:

TopTen Software Blog 上有一篇很棒的博客文章,其中包含有关通过 Interop 使用 ImageMagick graphics library 的 C# 代码。这篇文章专门讨论在单声道下在 linux 上运行 ASP.net;但是,C# 代码应该是完全可以复制粘贴的,如果您在引用窗口二进制文件 (DLL) 的 Windows 下运行,您唯一需要更改的是 Interop 属性。

ImageMagick® 是一个用于创建、编辑、合成或转换的软件套件 位图图像。它可以读取和写入各种格式的图像 (超过 100 种)包括 DPX、EXR、GIF、JPEG、JPEG-2000、PDF、PhotoCD、 PNG、Postscript、SVG 和 TIFF。使用 ImageMagick 调整大小、翻转、 镜像、旋转、扭曲、剪切和变换图像,调整图像 颜色,应用各种特殊效果,或绘制文本、线条、多边形, 椭圆和贝塞尔曲线。

codeplex 上还有一个ImageMagick .Net development project,它为您总结了所有内容。但它自 2009 年以来没有显示出积极的发展,因此它可能落后于当前的 ImageMagick 库版本。对于一个小的琐碎调整例程,我可能会坚持使用互操作。您只需要仔细观察您的实现是否存在您自己的内存泄漏或未释放的资源(库本身已经过良好的社区测试和审查)。

该库是免费和开源的。 Apache 2 许可证似乎与个人和商业目的兼容。见ImageMagick License Page

该库是完全跨平台的,并实现了许多 GDI+ 中没有的强大的图像处理和转换例程(或在 mono 下没有实现),并且作为 ASP.net 图像处理的替代品而享有盛誉。

更新:看起来这里有一个 .NET 包装器的更新版本:http://magick.codeplex.com/

【讨论】:

  • @DylanBeattie 我在低流量应用程序中使用了 imagemagick 的控制台对应部分,没有任何问题,所以您可能也想检查一下
  • 如果您使用 ImageMagick 并希望使用 PDF,那么您必须使用不一定是开源的 Ghostscript。
【解决方案2】:

是的,使用 WPF System.Windows.Media 类。完全管理它们不会遇到与 GDI 相同的问题。

这是我用来渲染渐变的一些 MVC 代码的摘录,让您了解如何从 WPF Visual 转换为 PNG:

using System;
using System.IO;
using System.Web.Mvc;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace MyMvcWebApp.Controllers
{
    public class ImageGenController : Controller
    {
        // GET: ~/ImageGen/Gradient?color1=red&color2=pink
        [OutputCache(CacheProfile = "Image")]
        public ActionResult Gradient(Color color1, Color color2, int width = 1, int height = 30, double angle = 90)
        {
            var visual = new DrawingVisual();
            using (DrawingContext dc = visual.RenderOpen())
            {
                Brush brush = new LinearGradientBrush(color1, color2, angle);
                dc.DrawRectangle(brush, null, new Rect(0, 0, width, height));
            }

            return new FileStreamResult(renderPng(visual, width, height), "image/png");
        }

        static Stream renderPng(Visual visual, int width, int height)
        {
            var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Default);
            rtb.Render(visual);

            var frame = BitmapFrame.Create(rtb);
            var encoder = new PngBitmapEncoder();
            encoder.Frames.Add(frame);

            var stream = new MemoryStream();
            encoder.Save(stream);
            stream.Position = 0;

            return stream;
        }
    }
}

【讨论】:

  • 它们不是完全托管的,它们是 WIC 的包装器,也不受支持(尽管文档说)。他们有大部分相同的问题以及新的问题。
  • WPF/WIC 在 ASP.NET 下也不支持。
【解决方案3】:

您可以在此处找到来自 Microsoft 员工的一篇非常好的文章:Resizing images from the server using WPF/WIC instead of GDI+,该文章建议使用 WPF 而不是 GDI+。它更多的是关于缩略图,但总体上是相同的问题。

不管怎样,最后是这样写的:

我联系了 WPF 团队,以最终确定这是否是 支持的。不幸的是,事实并非如此,文档正在 相应更新。对于这可能造成的任何混乱,我深表歉意 造成的。我们正在寻找方法让这个故事在 未来。

因此,Web 应用程序也不支持 WPF,我仍然相信:-S

【讨论】:

  • 是的,不支持。我用简单的 JPEG 调整大小失败了。
【解决方案4】:

图像锐化

ImageSharp 是一个开源的跨平台二维图形库。它是在新的 .NET 标准之上用 C# 编写的,不依赖于任何特定于操作系统的 API。

它目前仍处于 MyGet 上的预发布阶段(您必须在 VS 选项或 NuGet.config 文件中添加包源),但我们已经在使用它并取得了一些非常积极的结果。

【讨论】:

    【解决方案5】:

    我读到的大部分问题都与资源没有被正确处理有关。

    我一次又一次地使用此代码的变体,没有出现来自 Web 应用程序的问题:

    public void GenerateThumbNail(HttpPostedFile fil, string sPhysicalPath, 
                                  string sOrgFileName,string sThumbNailFileName,
                                  System.Drawing.Imaging.ImageFormat oFormat, int rez)
    {
    
        try
        {
    
            System.Drawing.Image oImg = System.Drawing.Image.FromStream(fil.InputStream);
    
            decimal pixtosubstract = 0;
            decimal percentage;
    
            //default
            Size ThumbNailSizeToUse = new Size();
            if (ThumbNailSize.Width < oImg.Size.Width || ThumbNailSize.Height < oImg.Size.Height)
            {
                if (oImg.Size.Width > oImg.Size.Height)
                {
                    percentage = (((decimal)oImg.Size.Width - (decimal)ThumbNailSize.Width) / (decimal)oImg.Size.Width);
                    pixtosubstract = percentage * oImg.Size.Height;
                    ThumbNailSizeToUse.Width = ThumbNailSize.Width;
                    ThumbNailSizeToUse.Height = oImg.Size.Height - (int)pixtosubstract;
                }
                else
                {
                    percentage = (((decimal)oImg.Size.Height - (decimal)ThumbNailSize.Height) / (decimal)oImg.Size.Height);
                    pixtosubstract = percentage * (decimal)oImg.Size.Width;
                    ThumbNailSizeToUse.Height = ThumbNailSize.Height;
                    ThumbNailSizeToUse.Width = oImg.Size.Width - (int)pixtosubstract;
                }
    
            }
            else
            {
                ThumbNailSizeToUse.Width = oImg.Size.Width;
                ThumbNailSizeToUse.Height = oImg.Size.Height;
            }
    
            Bitmap bmp = new Bitmap(ThumbNailSizeToUse.Width, ThumbNailSizeToUse.Height);
            bmp.SetResolution(rez, rez);
            System.Drawing.Image oThumbNail = bmp;
    
            bmp = null;
    
            Graphics oGraphic = Graphics.FromImage(oThumbNail);
    
            oGraphic.CompositingQuality = CompositingQuality.HighQuality;
    
            oGraphic.SmoothingMode = SmoothingMode.HighQuality;
    
            oGraphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
    
            Rectangle oRectangle = new Rectangle(0, 0, ThumbNailSizeToUse.Width, ThumbNailSizeToUse.Height);
    
            oGraphic.DrawImage(oImg, oRectangle);
    
            oThumbNail.Save(sPhysicalPath  + sThumbNailFileName, oFormat);
    
            oImg.Dispose();
    
        }
        catch (Exception ex)
        {
            Response.Write(ex.Message);
        }
    
    }
    

    【讨论】:

    • @rick:这是否意味着您正在调用的 System.Drawing 方法在 ASP.NET 应用程序中受支持?
    • 此函数中使用的 API 调用已在多个 Web 应用程序中大量使用,迄今为止我没有遇到任何问题。我不能确定它是否受到支持。
    • @rick:我认为 OP 发现的警告也适用于这些方法。随着 .NET 4.0 即将推出,“它总是有效的”变得不那么引人注目了。
    【解决方案6】:

    您可以查看http://gd-sharp.sourceforge.net/,它是 GD 库的包装器。我还没有测试过,但看起来很有希望。

    【讨论】:

      【解决方案7】:

      我在 ASP.Net 网络服务器环境中的 Cairo 库 (http://www.cairographics.org) 中表现良好。实际上,由于 WPF 对基于 Web 的东西的内存使用模型很差,我实际上从 WPF 搬到了 cairo。

      WPF 实际上往往会使您的工作进程耗尽内存。没有一个 WPF 对象实现IDisposable,并且它们中的许多都引用了只能通过终结器释放的非托管内存。大量使用 WPF(特别是如果您的服务器占用大量 CPU 资源)最终会耗尽您的内存,因为您的终结器队列已饱和。例如,当我分析我的应用程序时,终结队列上有超过 50,000 个对象,其中许多都持有对非托管内存的引用。 Cairo 对我来说表现得更好,它的内存使用模式比 WPF 更可预测。

      如果您对使用 cairo 感兴趣,请从 GTK+ 的网站获取库。它们具有 x86 和 x64 二进制文件集。

      唯一的缺点是 cairo 不能原生读/写 JPG;但是,您可以轻松地调整 WPF 的内容以读取/写入 JPG,并使用 Cairo 进行重新采样/缩放/绘图/其他任何操作。

      【讨论】:

        【解决方案8】:

        Aspose.Drawing 是 System.Drawing 的直接替代品,完全托管并可在 Web 应用程序中安全使用。 (我是开发人员之一。)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-12-29
          • 2010-11-10
          • 2020-03-17
          • 2012-01-09
          • 2016-02-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多