Pergunta

Depois de vários dias rastreando erros bizarros gdi+, eu tropecei nessa pequena jóia em Msdn:

As classes no espaço de nome System.Drawing não são suportadas para uso em um serviço Windows ou ASP.NET. Tentar usar essas classes de um desses tipos de aplicativos pode produzir problemas inesperados, como desempenho diminuído do serviço e exceções em tempo de execução.

Não sei se "Serviço ASP.NET" significa "Aplicativo da Web" neste contexto, mas "desempenho diminuído do serviço" certamente parece cobrir a variedade aleatória de "erros genéricos ocorreram em GDI+" e "fora da memória". que meu aplicativo está jogando - erros intermitentes e não reproduzíveis lendo e escrevendo imagens JPEG que - em muitos casos - foram realmente criadas por System.Drawing.Imaging.Imaging em primeiro lugar.

Então - se o GDI+ não conseguir ler e escrever arquivos JPEG de maneira confiável em um aplicativo da Web, o que devo usar?

Quero que os usuários possam fazer upload de imagens (JPEG necessário, outros formatos de bom de ter), reemboli-los confiável, e exibir mensagens de erro úteis se algo der errado. Alguma ideia? Os namespaces do System.Media valem a pena considerar?

EDITAR: Sim, eu sei que o GDI+ funciona "na maioria das vezes". Isso não é bom o suficiente, porque quando falha, o faz de uma maneira impossível isolar ou se recuperar de graciosamente. Não estou interessado em exemplos de código GDI+ que funcionem para você: estou procurando Bibliotecas alternativas a serem usadas para processamento de imagens.

Foi útil?

Solução

Há uma excelente postagem no blog, incluindo o código C# sobre o uso do IMAGEMAGICK GRAPHICS Library através da interoper Topten Software Blog. Esta postagem lida especificamente com a execução do ASP.NET no Linux sob Mono; No entanto, o código C# deve ser perfeitamente capaz de copiar, a única coisa que você precisará alterar são os atributos de interoper se você estiver executando no Windows referenciando um binário de janela (DLL).

O ImageMagick® é um conjunto de software para criar, editar, compor ou converter imagens de bitmap. Ele pode ler e escrever imagens em vários formatos (mais de 100), incluindo DPX, EXR, GIF, JPEG, JPEG-2000, PDF, PhotoCD, PNG, PostScript, SVG e TIFF. Use o ImageMagick para redimensionar, girar, espelhar, girar, distorcer, cisalhar e transformar imagens, ajustar as cores da imagem, aplicar vários efeitos especiais ou desenhar texto, linhas, polígonos, elipses e curvas de bézier.

Há também um ImageMagick .NET Development Project no codeplex que encerra tudo para você. Mas ele não mostra desenvolvimento ativo desde 2009, por isso pode estar atrasado na versão atual da Biblioteca ImageMagick. Para uma pequena rotina de redimensionamento trivial, eu provavelmente ficaria com a interopo. Você só precisa assistir à sua implementação cuidadosamente por seu próprio vazamento de memória ou recursos não lançados (a própria biblioteca é bem testada e examinada pela comunidade).

A biblioteca é gratuita e de código aberto. A licença Apache 2 parece ser compatível com fins pessoais e comerciais. Ver Página de licença ImageMagick.

A biblioteca é totalmente cruzada e implementa muitas rotinas poderosas de manuseio e transformação de imagens que não são encontradas no GDI+ (ou não implementadas em Mono) e tem uma boa reputação como uma alternativa para o processamento da imagem ASP.NET.

ATUALIZAÇÃO: Parece que há uma versão atualizada de um invólucro .NET aqui: http://magick.codeplex.com/

Outras dicas

Sim, use o WPF System.Windows.Media Aulas. Sendo totalmente gerenciados, eles não sofrem os mesmos problemas que as coisas do GDI.

Aqui está um trecho de algum código MVC que eu uso para renderizar gradientes, para dar uma idéia de como ir de um WPF Visual para um 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;
        }
    }
}

Você pode encontrar um artigo muito bom de um funcionário da Microsoft aqui: Redimensionar imagens do servidor usando WPF/WIC em vez de GDI+ Isso propõe usar o WPF em vez de GDI+. É mais sobre miniatura, mas no geral os mesmos problemas.

Enfim, no final, afirma isso:

Entrei em contato com a equipe do WPF para ter a palavra final sobre se isso é suportado. Infelizmente, não é, e a documentação está sendo atualizada de acordo. Peço desculpas por qualquer confusão que isso possa ter causado. Estamos procurando maneiras de tornar essa história mais aceitável no futuro.

Portanto, o WPF também não é suportado em aplicativos da web e ainda é acredito: -s

Imagesharp

Imagesharp é uma biblioteca gráfica 2D de plataforma cruzada de código aberto. Está escrito em C# no topo do novo padrão .NET, sem dependência de qualquer API específica do SO.

Atualmente, ele ainda está em pré-lançamento no myget (você precisará adicionar a fonte do pacote nas opções vs ou em um arquivo nuget.config), mas já o estamos usando com alguns resultados muito positivos.

A maioria das questões que li sobre pertencer aos recursos que não estão sendo descartados adequadamente.

Eu usei variantes deste código várias vezes sem problemas de aplicativos da 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);
    }

}

Você pode dar uma olhada http://gd-sharp.sourceforge.net/ que é um invólucro para a biblioteca GD. Eu não testei, mas parece promissor.

Eu tive um bom comportamento na Biblioteca do Cairo (http://www.cairographics.org) em um ambiente da Webs.net Web. Na verdade, mudei para o Cairo da WPF devido ao mau modelo de uso de memória do WPF para coisas baseadas na Web.

O WPF realmente tende a executar seu processo de trabalhador fora da memória. Nenhum dos objetos WPF implementam IDisposable, e muitos deles fazem referência à memória não gerenciada que só é libertada por meio de um finalizador. O uso pesado do WPF (especialmente se o seu servidor for significativamente em fase de CPU) acabará saindo fora da memória porque sua fila de finalizador ficar saturada. Quando eu estava perfilando meu aplicativo, por exemplo, a fila de finalização tinha mais de 50.000 objetos, muitos deles segurando referências à memória não gerenciada. O Cairo se comportou muito melhor para mim, e seu padrão de uso de memória tem sido muito mais previsível que o WPF.

Se você estiver interessado em usar o Cairo, pegue o site Libs no site do GTK+. Eles têm um X86, bem como um conjunto de binários x64.

A única desvantagem é que o Cairo não consegue ler/escrever JPG nativamente; No entanto, você pode adaptar facilmente as coisas do WPF para ler/escrever JPG e fazer o reamostragem/escala/desenho/o que mais usa o Cairo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top