33

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

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

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

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

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

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

4

8 回答 8

11

TopTen Software Blog上有一篇很棒的博客文章,其中包括关于通过 Interop使用ImageMagick 图形库的 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 开发项目,它为您包装了一切。但它自 2009 年以来没有显示出积极的发展,因此它可能落后于当前的 ImageMagick 库版本。对于一个很小的调整大小例程,我可能会坚持使用互操作。你只需要仔细观察你的实现,看看你自己的内存泄漏或未释放的资源(库本身已经过良好的测试和社区审查)。

该库是免费和开源的。Apache 2 许可证似乎与个人和商业目的兼容。请参阅ImageMagick 许可页面

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

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

于 2012-08-16T16:00:38.783 回答
9

是的,使用 WPFSystem.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;
        }
    }
}
于 2011-10-12T11:44:03.583 回答
9

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

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

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

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

于 2012-08-16T17:42:24.970 回答
4

图像锐化

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

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

于 2017-07-03T09:51:09.740 回答
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);
    }

}
于 2009-10-07T00:35:08.977 回答
0

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

于 2009-12-23T17:16:42.943 回答
0

我在 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 进行重新采样/缩放/绘图/其他任何操作。

于 2012-08-21T21:13:01.093 回答
-1

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

于 2020-11-07T07:09:44.120 回答