用于ASP.NET的System.Drawing的替代方案?

时间:2009-10-06 22:56:26

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

经过几天跟踪奇怪的GDI +错误,我偶然发现MSDN上的这个小宝石:

  

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

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

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

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

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

8 个答案:

答案 0 :(得分:10)

有一篇很棒的博客文章,其中包含关于在ImageMagick graphics library使用TopTen Software Blog通过Interop的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 +中找不到(或者不在单声道下实现),并且作为ASP.net图像处理的替代品具有良好的声誉。

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

答案 1 :(得分:9)

是的,使用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;
        }
    }
}

答案 2 :(得分:9)

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

无论如何,最后它说明了这一点:

  

我联系了WPF团队,对这是否是最终决定   支持的。不幸的是,它不是,文档正在存在   相应更新。我为这可能产生的任何混淆道歉   造成的。我们正在研究如何让这个故事更容易接受   未来。

所以WPF在网络应用程序中也不受支持,我仍然相信:-S

答案 3 :(得分:4)

ImageSharp

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

目前它仍然在MyGet上发布(您必须在VS选项或NuGet.config文件中添加软件包源代码),但我们已经使用了一些非常积极的内容结果

答案 4 :(得分:1)

我读过的大多数问题都与资源配置不当有关。

我已经一次又一次地使用了这段代码的变体而没有来自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);
    }

}

答案 5 :(得分:0)

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

答案 6 :(得分:0)

我在ASP.Net网络服务器环境中的开罗图书馆(http://www.cairographics.org)表现得很好。我实际上是从WPF迁移到cairo,因为WPF的内存使用模型很差,基于网络的东西。

WPF实际上往往会使您的工作进程无法运行。 WPF对象都没有实现IDisposable,并且其中许多对象引用了仅通过终结器释放的非托管内存。大量使用WPF(特别是如果你的服务器显着的CPU负担)将最终使你的内存不足,因为你的终结器队列已经饱和。例如,当我分析我的应用程序时,终结队列上有超过50,000个对象,其中许多都持有对非托管内存的引用。开罗对我来说表现得更好,其内存使用模式比WPF更容易预测。

如果您对使用cairo感兴趣,请从GTK +的网站上获取库。它们有一个x86以及一组x64二进制文件。

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

答案 7 :(得分:0)

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