¿Alternativas a System.Drawing para usar con ASP.NET?

Después de varios días de rastrear los extraños errores de GDI +, me encontré con esta pequeña joya en MSDN :

Las clases dentro del espacio de nombres System.Drawing no son compatibles para su uso dentro de un servicio de Windows o ASP.NET. Intentar utilizar estas clases desde uno de estos tipos de aplicaciones puede producir problemas inesperados, como una disminución en el rendimiento del servicio y excepciones en el tiempo de ejecución.

No sé si “servicio ASP.NET” significa “aplicación web” en este contexto, pero “rendimiento del servicio disminuido” ciertamente parece cubrir el surtido aleatorio de “Se produjo un error genérico en GDI +” y “Errores de memoria” que mi aplicación está lanzando: errores intermitentes, no reproducibles, lectura y escritura de imágenes JPEG que, en muchos casos, fueron creadas por System.Drawing.Imaging en primer lugar.

Entonces, si GDI + no puede leer y escribir archivos JPEG de manera confiable en una aplicación web, ¿qué debería usar en su lugar?

Quiero que los usuarios puedan subir imágenes (requiere JPEG, otros formatos agradables para tener), volver a muestrearlas de manera confiable y mostrar mensajes de error útiles si algo sale mal. ¿Algunas ideas? ¿Vale la pena considerar los espacios de nombres System.Media de WPF?

Gracias,

Dylan

EDITAR: Sí, sé que GDI + funciona “la mayor parte del tiempo”. Eso no es lo suficientemente bueno, porque cuando falla, lo hace de una manera que es imposible aislar o recuperar con gracia. No estoy interesado en ejemplos de código GDI + que funcione para usted: estoy buscando bibliotecas alternativas para usar en el procesamiento de imágenes.

Existe una excelente publicación de blog que incluye el código C # sobre el uso de la biblioteca de gráficos ImageMagick a través de Interop en TopTen Software Blog . Esta publicación trata específicamente de ejecutar ASP.net en Linux bajo mono; sin embargo, el código de C # debe ser perfectamente compatible con copiar y pegar, lo único que tendrá que cambiar es los atributos de Interoperación si se está ejecutando en Windows haciendo referencia a un binario de ventana (DLL).

ImageMagick® es un paquete de software para crear, editar, componer o convertir imágenes de bitmap. Puede leer y escribir imágenes en una variedad de formatos (más de 100) incluyendo DPX, EXR, GIF, JPEG, JPEG-2000, PDF, PhotoCD, PNG, Postscript, SVG y TIFF. Utilice ImageMagick para cambiar el tamaño, voltear, reflejar, girar, distorsionar, cortar y transformar imágenes, ajustar los colores de la imagen, aplicar diversos efectos especiales o dibujar texto, líneas, polígonos, elipses y curvas de Bézier.

También hay un proyecto de desarrollo ImageMagick .Net en codeplex que lo envuelve todo por ti. Pero no muestra el desarrollo activo desde 2009, por lo que puede estar rezagado con respecto a la versión actual de la biblioteca de ImageMagick. Para una pequeña rutina de cambio de tamaño trivial, probablemente me quedo con la interoperabilidad. Solo necesita ver su implementación cuidadosamente para su propia pérdida de memoria o recursos inéditos (la biblioteca en sí está bien probada y examinada por la comunidad).

La biblioteca es gratuita y de código abierto. La licencia de Apache 2 parece ser compatible tanto con fines personales como comerciales. Ver la página de licencia de ImageMagick .

La biblioteca es totalmente multiplataforma e implementa muchas rutinas poderosas de manejo y transformación de imágenes que no se encuentran en GDI + (o no se implementan en mono) y tiene una buena reputación como alternativa para el procesamiento de imágenes ASP.net.

Actualización: parece que hay una versión actualizada de .NET wrapper aquí: http://magick.codeplex.com/

Sí, use las clases WPF System.Windows.Media . Al estar completamente administrados, no sufren los mismos problemas que las cosas de GDI.

Aquí hay un extracto de algún código MVC que utilizo para representar degradados, para darle una idea de cómo pasar de un WPF Visual a un 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; } } } 

Aquí puede encontrar un artículo muy bueno de un empleado de Microsoft: Cambiar el tamaño de las imágenes del servidor utilizando WPF / WIC en lugar de GDI + que propone utilizar WPF en lugar de GDI +. Se trata más de miniaturas, pero en general son los mismos problemas.

De todos modos, al final dice esto:

Me puse en contacto con el equipo de WPF para tener la última palabra sobre si esto es compatible. Lamentablemente, no es así, y la documentación se está actualizando en consecuencia. Me disculpo por cualquier confusión que esto pueda haber causado. Estamos buscando formas de hacer que esa historia sea más aceptable en el futuro.

Entonces WPF tampoco está soportado en aplicaciones web y todavía creo: -S

ImageSharp

ImageSharp es una biblioteca de gráficos 2D multiplataforma de fuente abierta. Está escrito en C # en la parte superior del nuevo .NET Standard, sin dependencia de ninguna API específica del sistema operativo.

Todavía está en prelanzamiento en MyGet (tendrá que agregar el origen del paquete en las opciones VS o en un archivo NuGet.config), pero ya lo estamos usando con algunos resultados muy positivos.

La mayoría de los problemas que he leído sobre los recursos no se eliminan adecuadamente.

He utilizado variantes de este código una y otra vez sin problemas de las aplicaciones 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); } } 

Puede echar un vistazo a http://gd-sharp.sourceforge.net/ que es un contenedor para la biblioteca de GD. No lo he probado, pero parece prometedor.

He tenido un buen comportamiento de la biblioteca de El Cairo (http://www.cairographics.org) en un entorno de servidor web ASP.Net. De hecho, me mudé a El Cairo desde WPF debido al pobre modelo de uso de memoria de WPF para cosas basadas en la web.

WPF en realidad tiende a ejecutar su proceso de trabajo sin memoria. Ninguno de los objetos WPF implementa IDisposable , y muchos de ellos hacen referencia a la memoria no administrada que solo se libera mediante un finalizador. El uso intensivo de WPF (especialmente si su servidor tiene un impuesto significativo sobre la CPU) eventualmente lo dejará sin memoria porque la cola del finalizador se satura. Cuando estaba perfilando mi aplicación, por ejemplo, la cola de finalización tenía más de 50,000 objetos en ella, muchos de ellos con referencias a la memoria no administrada. El Cairo se ha comportado mucho mejor para mí, y su patrón de uso de memoria ha sido mucho más predecible que el de WPF.

Si estás interesado en usar El Cairo, toma las librerías del sitio web de GTK +. Tienen un x86, así como un conjunto de binarios x64.

El único inconveniente es que cairo no puede leer / escribir JPG de forma nativa; sin embargo, puedes adaptar fácilmente las cosas de WPF para leer / escribir JPG y hacer el remuestreo / escala / dibujo / lo que sea usando Cairo.