Pregunta

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

Las clases dentro del sistema System. El espacio de nombres no es compatible para su uso dentro de un servicio Windows o ASP.NET. Intentar usar estas clases desde uno de estos tipos de aplicaciones puede producir problemas inesperados, como el rendimiento del servicio disminuido y las excepciones de tiempo de ejecución.

No sé si el "servicio ASP.NET" significa "aplicación web" en este contexto, pero el "rendimiento del servicio disminuido" ciertamente parece cubrir la variedad aleatoria de "un error genérico ocurrió en GDI+" y "fuera de memoria" errores Que mi aplicación está lanzando: errores intermitentes y no reproducibles que leen y escriben imágenes JPEG que, en muchos casos, fueron creadas por System.Drawing. Iiming en primer lugar.

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

Quiero que los usuarios puedan cargar imágenes (requerido JPEG, otros formatos agradables), vuelva a muestrearlas seguramente, y muestre mensajes de error útiles si algo sale mal. ¿Algunas ideas? ¿Vale la pena considerar los espacios de nombres System.Media de WPF?

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 recuperarse con gracia. No estoy interesado en ejemplos de código GDI+ que funcione para ti: estoy buscando Bibliotecas alternativas para usar para el procesamiento de imágenes.

¿Fue útil?

Solución

Hay una excelente publicación de blog que incluye código C# sobre el uso de Biblioteca de gráficos ImageMagick a través de Interop over en Blog de software Topten. Esta publicación trata específicamente con la ejecución de ASP.NET en Linux en Mono; Sin embargo, el código C# debe ser perfectamente Copy-Paste, lo único que necesitará cambiar son los atributos INETOP si se ejecuta en Windows haciendo referencia a un binario de ventana (DLL).

ImageMagick® es un conjunto de software para crear, editar, componer o convertir imágenes de mapa de bits. Puede leer y escribir imágenes en una variedad de formatos (más de 100), incluidos DPX, EXR, GIF, JPEG, JPEG-2000, PDF, PhotOCD, PNG, PostScript, SVG y TIFF. Use ImageMagick para cambiar el tamaño, voltee, reflejen, gire, distorsione, cense y transforme imágenes, ajuste los colores de la imagen, aplique varios efectos especiales o dibuje texto, líneas, polígonos, elipses y curvas Bézier.

También hay un ImageMagick .NET Development Project en CodePlex que envuelve todo para ti. Pero no muestra un desarrollo activo desde 2009, por lo que puede estar retrasado detrás de la versión actual de la biblioteca ImageMagick. Para una pequeña rutina de cambio de tamaño trivial, probablemente me quedaría con el interop. Solo necesita observar su implementación cuidadosamente para su propia fuga de memoria o recursos inéditos (la comunidad es bien probada y examinada por la comunidad).

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

La biblioteca es una plataforma cruzada totalmente e implementa muchas potentes rutinas 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 A .NET Wrapper aquí: http://magick.codeplex.com/

Otros consejos

Sí, usa el WPF System.Windows.Media clases. Al ser manejados por completo, no sufren los mismos problemas que las cosas GDI.

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

Puede encontrar un muy buen artículo de un empleado de Microsoft aquí: Restablecer imágenes del servidor usando WPF/WIC en lugar de GDI+ Eso propone usar WPF en lugar de GDI+. Se trata más de miniatura, pero en general es 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. Desafortunadamente, no lo es, y la documentación se actualiza en consecuencia. Pido disculpas 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á compatible en aplicaciones web y todavía lo creo: -S

Cuadro de imágenes

Cuadro de imágenes es una biblioteca de gráficos 2D multiplataforma de código abierto. Está escrito en C# en la parte superior del nuevo estándar .NET, sin dependencia de ninguna API específica del sistema operativo.

Actualmente todavía está en prelanzamiento en Myget (tendrá que agregar la fuente 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 sobre los que he leído pertenecen a los recursos que no se están dispuestos correctamente.

He usado variantes de este código una y otra vez sin problemas de 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 envoltorio para la biblioteca GD. No lo he probado, pero parece prometedor.

He tenido un buen comportamiento de la Biblioteca 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 mal modelo de uso de memoria de WPF para cosas basadas en la web.

WPF en realidad tiende a ejecutar su proceso de trabajo fuera de la memoria. Ninguno de los objetos WPF implementan IDisposable, y muchos de ellos hacen referencia a la memoria no administrada que solo se libera a través de un finalizador. El uso intensivo de WPF (especialmente si su servidor está significativamente impuesto por CPU) eventualmente lo ejecutará fuera de la memoria porque su cola de finalizador se satura. Cuando estaba perfilando mi aplicación, por ejemplo, la cola de finalización tenía más de 50,000 objetos, muchos de ellos teniendo 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á interesado en usar El Cairo, tome las libs del sitio web de GTK+. Tienen un X86, así como un conjunto de binarios x64.

El único inconveniente es que El Cairo no puede leer/escribir JPG de forma nativa; Sin embargo, podría adaptar fácilmente las cosas de WPF para leer/escribir JPG y hacer el remuestreo/escala/dibujo/cualquier otra cosa con El Cairo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top