经过几天跟踪奇怪的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 +代码示例不感兴趣:我正在寻找用于图像处理的替代库。
答案 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是一个开源的跨平台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应用程序中安全使用。 (我是开发人员之一。)