Ocorreu um erro genérico no GDI +, JPEG Image to MemoryStream

326

Parece ser um erro infame em toda a web. Tanto que não consegui encontrar uma resposta para o meu problema, pois meu cenário não se encaixa. Uma exceção é lançada quando eu salvo a imagem no fluxo.

Estranhamente, isso funciona perfeitamente com um png, mas gera o erro acima com jpg e gif, o que é bastante confuso.

O problema mais parecido está relacionado ao salvamento de imagens em arquivos sem permissão. Ironicamente, a solução é usar um fluxo de memória como eu estou fazendo ....

public static byte[] ConvertImageToByteArray(Image imageToConvert)
{
    using (var ms = new MemoryStream())
    {
        ImageFormat format;
        switch (imageToConvert.MimeType())
        {
            case "image/png":
                format = ImageFormat.Png;
                break;
            case "image/gif":
                format = ImageFormat.Gif;
                break;
            default:
                format = ImageFormat.Jpeg;
                break;
        }

        imageToConvert.Save(ms, format);
        return ms.ToArray();
    }
}

Mais detalhes à exceção. A razão pela qual isso causa tantos problemas é a falta de explicação :(

System.Runtime.InteropServices.ExternalException was unhandled by user code
Message="A generic error occurred in GDI+."
Source="System.Drawing"
ErrorCode=-2147467259
StackTrace:
   at System.Drawing.Image.Save(Stream stream, ImageCodecInfo encoder, EncoderParameters    encoderParams)
   at System.Drawing.Image.Save(Stream stream, ImageFormat format)
   at Caldoo.Infrastructure.PhotoEditor.ConvertImageToByteArray(Image imageToConvert) in C:\Users\Ian\SVN\Caldoo\Caldoo.Coordinator\PhotoEditor.cs:line 139
   at Caldoo.Web.Controllers.PictureController.Croppable() in C:\Users\Ian\SVN\Caldoo\Caldoo.Web\Controllers\PictureController.cs:line 132
   at lambda_method(ExecutionScope , ControllerBase , Object[] )
   at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClassa.<InvokeActionMethodWithFilters>b__7()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
 InnerException: 

OK coisas que eu tentei até agora.

  1. Clonando a imagem e trabalhando nisso.
  2. Recuperando o codificador para esse MIME passando isso com a configuração de qualidade jpeg.
madcapnmckay
fonte
related: stackoverflow.com/questions/4671449/...
Patrick Szalapski
3
Para mim, o problema era que a pasta não existia. Corrigido apenas criando a pasta.
21917 hazjack
Para mim, foi um índice fora de alcance sendo engolido.
Billy Jake O'Connor

Respostas:

189

OK, parece que encontrei a causa apenas por pura sorte e não há nada de errado com esse método específico, é ainda mais o backup da pilha de chamadas.

Anteriormente, redimensiono a imagem e, como parte desse método, retorno o objeto redimensionado da seguinte maneira. Eu inseri duas chamadas para o método acima e uma gravação direta em um arquivo.

// At this point the new bitmap has no MimeType
// Need to output to memory stream
using (var m = new MemoryStream())
{
       dst.Save(m, format);

       var img = Image.FromStream(m);

       //TEST
       img.Save("C:\\test.jpg");
       var bytes = PhotoEditor.ConvertImageToByteArray(img);


       return img;
 }

Parece que o fluxo de memória em que o objeto foi criado precisa ser aberto no momento em que o objeto é salvo. Não sei por que isso acontece. Alguém é capaz de me esclarecer e como posso contornar isso.

Eu só retorno de um fluxo porque, depois de usar o código de redimensionamento semelhante a esse, o arquivo de destino tem um tipo mime desconhecido (img.RawFormat.Guid) e o ID do tipo Mime está correto em todos os objetos de imagem, o que dificulta a escrita genérica código de manipulação caso contrário.

EDITAR

Isso não apareceu na minha pesquisa inicial, mas aqui está a resposta de Jon Skeet

madcapnmckay
fonte
4
Não percebi que quando você obtém um bitmap de um fluxo de memória, não deve fechá-lo. muito útil, obrigado
mcdon
38
Obrigado. Isso provavelmente salvou o resto do meu cabelo.
NotMe
6
Obrigado! isso me salvou muito tempo, uma coisa, no entanto, você se importaria de destacar a causa do erro no início de sua resposta, pois eu (e acho que a maioria dos falks) perdeu o skim original nas respostas, talvez algo como " não feche a MEMÓRIA de fluxo se você pretende usar novamente a imagem" seria ótimo; D
DORD
6
Qual é a sua variável "dst"?
WEFX 14/05
1
@madcapnmckay por favor explicar o que a variável 'dst' é e seu significado
Mike T
131

Se você está recebendo esse erro, posso dizer que seu aplicativo não tem permissão de gravação em algum diretório.

Por exemplo, se você estiver tentando salvar a imagem do fluxo de memória no sistema de arquivos, poderá receber esse erro.

Por favor, se você estiver usando o XP, adicione permissão de gravação para a conta aspnet nessa pasta.

Se você estiver usando o Windows server (2003,2008) ou o Vista, certifique-se de adicionar permissão de gravação à conta do serviço de rede.

Espero que ajude alguém.

Savindra
fonte
7
Você não fez! Perdi duas horas com as malditas permissões de gravação ... Vim aqui para postar isso. Espero que você receba mais votos positivos. :)
Gleno
2
Esta foi a solução para mim. +1 totalmente!
Grandizer 19/09/12
5
Você pode executar File.WriteAllText ("filename.jpg", "") e depois File.DeleteFile ("filename.jpg") antes de salvar o bitmap. Na minha marca, isso leva apenas 0,001 segundos e você recebe um bom 'Você não tem permissão para salvar o nome do arquivo.jpg lá'
Despertar
@ Despertar Você quer dizer File.Delete (), mas esse é um truque muito útil! Definitivamente vou usar isso sempre que eu salvar um bitmap.
D Coetzee
2
No meu caso, o diretório não existia.
silencedmessage
54

Acrescentarei também a causa do erro, na esperança de ajudar algum futuro viajante da Internet. :)

GDI + limita a altura máxima de uma imagem a 65500

Fazemos um redimensionamento básico da imagem, mas, ao redimensionar, tentamos manter a proporção. Temos um cara de controle de qualidade que é um pouco bom demais nesse trabalho; ele decidiu testar isso com uma foto com UM pixel de largura e 480 pixels de altura. Quando a imagem foi dimensionada para atender às nossas dimensões, a altura estava ao norte de 68.000 pixels e nosso aplicativo explodiu comA generic error occurred in GDI+ .

Você pode verificar isso sozinho com o teste:

  int width = 480;
  var height = UInt16.MaxValue - 36; //succeeds at 65499, 65500
  try
  {
    while(true)
    {
      var image = new Bitmap(width, height);
      using(MemoryStream ms = new MemoryStream())
      {
        //error will throw from here
        image.Save(ms, ImageFormat.Jpeg);
      }
      height += 1;
    }
  }
  catch(Exception ex)
  {
    //explodes at 65501 with "A generic error occurred in GDI+."
  }

É uma pena que não haja um .net amigável ArgumentExceptionlançado no construtor de Bitmap.

Fred
fonte
17
Obrigado - este viajante da Internet na Internet agradece por você ter deixado esta mensagem.
Tom Oeste
Do meu teste, 65535 é realmente o valor máximo. No 65536, começo a ver o erro genérico.
precisa saber é o seguinte
Tentei novamente: Win10 .net 4.5 e .net 4.6.1, e explodiu em 65501, o que parece ainda mais aleatório. Código também é repleta de erros de sintaxe, irá atualizar :)
Fred
37

Este artigo explica em detalhes o que exatamente acontece: Dependências do construtor Bitmap e Image

Em suma, por toda a vida de um Imageconstruído a partir de um fluxo , o fluxo não deve ser destruído.

Então, ao invés de

using (var strm = new ... )  {
    myImage = Image.FromStream(strm);
}

tente isso

Stream imageStream;
...

    imageStream = new ...;
    myImage = Image.FromStream(strm);

e feche o imageStream no fechamento do formulário ou na página da web.

Ivan Mesic
fonte
Sim, este me pegou. Eu estava sendo consciente e envolvi meu fluxo em uma usingtentativa de, posteriormente, copiar a imagem em um fluxo de memória e recebi a terrível mensagem "Erro genérico no GDI +".
que você precisa
Seu link estava me dando redirecionamentos infinitos; este funciona. Eu estava tendo um problema ao salvar, PixelFormat.Format32bppArgbmas não estava PixelFormat.Format1bppIndexed. O artigo vinculado explica o motivo: o GDI + pode optar por decodificar novamente os dados de bitmap do fluxo de origem, em vez de manter tudo na memória. Meu palpite é que ele não decodifica as imagens 1bpp.
labreuer
Até o novo link não funciona mais. Uma simples pesquisa no Google não pareceu revelar a página correta. Mas fiquei muito feliz em encontrar esta resposta! Meu trabalho-around copiando para um novo bitmap falhou devido a esta questão
Katjoek
28

Você também receberá essa exceção se tentar salvar em um caminho inválido ou se houver um problema de permissão.

Se você não tem 100% de certeza de que o caminho do arquivo está disponível e as permissões estão corretas, tente gravar um em um arquivo de texto. Demora apenas alguns segundos para descartar o que seria uma correção muito simples.

var img = System.Drawing.Image.FromStream(incomingStream);

// img.Save(path);
System.IO.File.WriteAllText(path, "Testing valid path & permissions.");

E não se esqueça de limpar seu arquivo.

Kirk Broadhurst
fonte
Esse foi o problema para mim ... Eu gostaria que o erro fosse menos vago, teria me poupado muito tempo.
Oofpez
Sim! A pasta na qual você está salvando deve existir. Agora, faço uma verificação antes disso, antes de tentar salvar uma imagem. (Ainda assim, o erro chama-me para fora, uma vez por ano.)
Magnus Smith
Meu caminho era um diretório, em vez de um arquivo.
Asen Kasimov
20

Salvar imagem na variável de bitmap

using (var ms = new MemoryStream())
{
    Bitmap bmp = new Bitmap(imageToConvert);
    bmp.Save(ms, format);
    return ms.ToArray();
}
Amir Atashin
fonte
Isso resolveu meu problema. Você poderia explicar por que salvar a imagem no Bitmap afasta a exceção?
JMC
Salvei meu dia .. não sei o que causou o problema, mas o Bitmap save funciona. O System.Drawing.Image não será salvo no fluxo de memória, mas o Bitmap !!!
San
Esta foi a melhor solução para mim. Criando novo Bitmap e convertendo a partir dele.
uzay95
17

Caso alguém esteja fazendo coisas tão estúpidas quanto eu. 1. verifique se o caminho existe. 2. verifique se você tem permissões para escrever. 3. verifique se o caminho está correto, no meu caso estava faltando o nome do arquivo no TargetPath :(

deveria ter dito, seu caminho é péssimo do que "Ocorreu um erro genérico no GDI +"

ahsant
fonte
16

Também recebi esse erro ao salvar JPEGs, mas apenas para determinadas imagens.

Meu código final:

  try
  {
    img.SaveJpeg(tmpFile, quality); // This is always successful for say image1.jpg, but always throws the GDI+ exception for image2.jpg
  }
  catch (Exception ex)
  {
    // Try HU's method: Convert it to a Bitmap first
    img = new Bitmap(img); 
    img.SaveJpeg(tmpFile, quality); // This is always successful
  }

Eu não criei as imagens, então não sei dizer qual é a diferença.
Eu apreciaria se alguém pudesse explicar isso.

Esta é a minha função SaveJpeg apenas FYI:

private static void SaveJpeg(this Image img, string filename, int quality)
{
  EncoderParameter qualityParam = new EncoderParameter(Encoder.Quality, (long)quality);
  ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
  EncoderParameters encoderParams = new EncoderParameters(1);
  encoderParams.Param[0] = qualityParam;
  img.Save(filename, jpegCodec, encoderParams);
}

private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
    var encoders = ImageCodecInfo.GetImageEncoders();
    var encoder = encoders.SingleOrDefault(c => string.Equals(c.MimeType, mimeType, StringComparison.InvariantCultureIgnoreCase));
    if (encoder == null) throw new Exception($"Encoder not found for mime type {mimeType}");
    return encoder;
}
Aximili
fonte
1
Isso resolveu dias de puxar o cabelo. É o código mais wtf eu acho que eu já escrevi :)
Jeff Dunlop
13

Descobri que, se uma das pastas pai em que eu estava salvando o arquivo tivesse um espaço à direita, o GDI + lançaria a exceção genérica.

Em outras palavras, se eu tentasse salvar em "C: \ Documents and Settings \ nome do usuário \ Configurações locais \ Temp \ ABC DEF M1 Valores de tendência \ Images \ picture.png", lançaria a exceção genérica.

O nome da minha pasta estava sendo gerado a partir de um nome de arquivo que tinha um espaço à direita, por isso foi fácil .Trim () e seguir em frente.

Igilima
fonte
3
incrível - Eu nunca teria pensado em olhar para o caminho do diretório que de perto
jharr100
11

se o seu código for o seguinte, também ocorrerá este erro

private Image GetImage(byte[] byteArray)
{
   using (var stream = new MemoryStream(byteArray))
   {
       return Image.FromStream(stream);
    }
}

O correto é

private Image GetImage(byte[] byteArray)
{
   var stream = new MemoryStream(byteArray))
   return Image.FromStream(stream);        
}

Isso pode ser porque estamos retornando do bloco using

dhinesh
fonte
para mim foi o retorno no bloco de uso. Eu ainda uso usando, mas retorno o valor fora do bloco. obrigado!
Dragouf 18/11/10
1
Descobri "da maneira mais difícil" que, se você estiver salvando novamente essa imagem em um novo fluxo (como HttpContext.Response.OutputStream, por exemplo), será necessário também fazer um fluxo.Flush (), se não ocorrer o erro novamente.
Lucian
11

Esta é uma expansão / qualificação da resposta de Fred, que declarou: "A GDI limita a altura de uma imagem a 65534". Deparamos com esse problema com um de nossos aplicativos .NET e, depois de ver o post, nossa equipe de terceirização levantou as mãos e disse que não poderia resolver o problema sem grandes alterações.

Com base nos meus testes, é possível criar / manipular imagens com uma altura maior que 65534, mas o problema surge ao salvar em um fluxo ou arquivo EM CERTOS FORMATOS . No código a seguir, a chamada do método t.Save () lança nosso amigo a exceção genérica quando a altura do pixel é 65501 para mim. Por motivos de curiosidade, repeti o teste de largura e o mesmo limite foi aplicado à economia.

    for (int i = 65498; i <= 100000; i++)
    {
        using (Bitmap t = new Bitmap(800, i))
        using (Graphics gBmp = Graphics.FromImage(t))
        {
            Color green = Color.FromArgb(0x40, 0, 0xff, 0);
            using (Brush greenBrush = new SolidBrush(green))
            {
                // draw a green rectangle to the bitmap in memory
                gBmp.FillRectangle(greenBrush, 0, 0, 799, i);
                if (File.Exists("c:\\temp\\i.jpg"))
                {
                    File.Delete("c:\\temp\\i.jpg");
                }
                t.Save("c:\\temp\\i.jpg", ImageFormat.Jpeg);
            }
        }
        GC.Collect();
    }

O mesmo erro também ocorre se você gravar em um fluxo de memória.

Para contornar isso, você pode repetir o código acima e substituir ImageFormat.Tiff ou ImageFormat.Bmp por ImageFormat.Jpeg.

Isso atinge alturas / larguras de 100.000 para mim - eu não testei os limites. Por acaso .Tiff era uma opção viável para nós.

ESTEJA AVISADO

Os fluxos / arquivos TIFF na memória consomem mais memória do que seus equivalentes JPG.

vipes
fonte
10

Teve um problema muito semelhante e também tentou clonar a imagem que não funciona. Eu descobri que a melhor solução era criar um novo objeto Bitmap a partir da imagem que foi carregada do fluxo de memória. Dessa forma, o fluxo pode ser descartado, por exemplo

using (var m = new MemoryStream())
{
    var img = new Bitmap(Image.FromStream(m));
    return img;
}

Espero que isto ajude.

HU.
fonte
6

Ocorreu um erro devido à permissão. verifique se a pasta possui TODA A PERMISSÃO.

public Image Base64ToImage(string base64String)
    {
        // Convert Base64 String to byte[]
        byte[] imageBytes = Convert.FromBase64String(base64String);
        MemoryStream ms = new MemoryStream(imageBytes, 0,
          imageBytes.Length);

        // Convert byte[] to Image
        ms.Write(imageBytes, 0, imageBytes.Length);
        Image image = Image.FromStream(ms, true);
        return image;
    }

 img.Save("YOUR PATH TO SAVE IMAGE")
Gaurang s
fonte
Eu concordo com você.
Resolvi
5

RESOLVIDO - Eu tive esse problema exato. A correção, para mim, foi aumentar a cota de disco para o IUSR no servidor IIS. Nesse caso, temos um aplicativo de catálogo com imagens de itens e coisas do gênero. A cota de upload para o "Usuário da Web anônimo" foi definida como 100 MB, que é o padrão para os servidores IIS desta empresa de hospedagem. Aumentei para 400 MB e consegui enviar imagens sem erros.

Talvez esse não seja o seu problema, mas se for, é uma solução fácil.

Marco
fonte
4

No meu caso, o problema estava no caminho que eu estava salvando (a raiz C:\). Mudá-lo para D:\111\fazer a exceção desaparecer.

Ani
fonte
4

Outra causa para esse erro - o caminho que você indica no método Save da instância de Bitmap não existe ou você não forneceu um caminho completo / válido.

Só tive esse erro porque estava passando um nome de arquivo e não um caminho completo!

Acontece!

MytyMyky
fonte
4

Minha vez!

using (System.Drawing.Image img = Bitmap.FromFile(fileName))
{
      ... do some manipulation of img ...
      img.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);
}

Consegui no .Save ... porque o using () está mantendo o arquivo aberto, então não posso substituí-lo. Talvez isso ajude alguém no futuro.

Andy
fonte
4

Mesmo problema que eu estava enfrentando. Mas no meu caso, eu estava tentando salvar um arquivo na unidade C e não estava acessível. Então, eu tentei salvar no drive D, que era totalmente acessível e consegui.

Portanto, verifique primeiro as pastas nas quais você está tentando salvar. Você deve ter todos os direitos (leitura e gravação) para essa pasta específica.

Jaimin
fonte
causa normalmente c não permite sem permissão do administrador.
Aneeq Azam Khan
2

Percebo que o seu caso "jpeg" é realmente:

            default:
                format = ImageFormat.Jpeg;
                break;

Tem certeza de que o formato é JPEG e não outra coisa?

Eu tentaria:

            case "image/jpg": // or "image/jpeg" !
                format = ImageFormat.Jpeg;
                break;

Ou verifique o que imageToConvert.MimeType()realmente está retornando.

ATUALIZAR

Existe alguma outra inicialização que você precisa fazer no objeto MemoryStream?

ChrisF
fonte
Obrigado. Definitivamente, está sendo chamado com o formato correto. Carrego um jpg, depuro e confirmo que o mime é reconhecido como image / jpeg e o formato é JPG.
27415 madcapnmckay
3
Oh bem - eu sempre tento eliminar o óbvio primeiro. Não consigo contar o número de vezes que não fiz isso e voltou a me morder mais tarde.
ChrisF
2
  • Eu tive esse problema em um servidor de teste, mas não no servidor ativo.
  • Eu estava gravando a imagem em um fluxo, por isso não era um problema de permissão.
  • Eu estava implantando diretamente algumas das DLLs no servidor de teste.
  • A implantação de toda a solução corrigiu o problema, portanto, provavelmente era uma incompatibilidade estranha de compilação
Chris Halcrow
fonte
2

Apenas para lançar outra solução possível na pilha, mencionarei o caso em que encontrei essa mensagem de erro. O método Bitmap.Savelançaria essa exceção ao salvar um bitmap que eu havia transformado e estava exibindo. Eu descobri que isso não causaria a exceção se a declaração tivesse um ponto de interrupção, nem o Bitmap.Saveprecederia, Thread.Sleep(500)então suponho que haja algum tipo de contenção de recursos em andamento.

Simplesmente copiar a imagem para um novo objeto Bitmap foi suficiente para impedir que essa exceção apareça:

new Bitmap(oldbitmap).Save(filename);
Segfault
fonte
2

Tivemos um problema semelhante ao gerar uma PDFimagem ou redimensionar usando o ImageProcessor lib no servidor de produção.

Recicle o pool de aplicativos, corrija o problema.

tech-gayan
fonte
1

Se você estiver tentando salvar uma imagem em um local remoto, adicione a NETWORK_SERVICEconta do usuário nas configurações de segurança e conceda a ele permissões de leitura e gravação. Caso contrário, não vai funcionar.

JAH
fonte
1
byte[] bts = (byte[])page1.EnhMetaFileBits; 
using (var ms = new MemoryStream(bts)) 
{ 
    var image = System.Drawing.Image.FromStream(ms); 
    System.Drawing.Image img = image.GetThumbnailImage(200, 260, null, IntPtr.Zero);      
    img.Save(NewPath, System.Drawing.Imaging.ImageFormat.Png);
}
jeka
fonte
1

Simples, criar uma nova instância do Bitmap resolve o problema.

string imagePath = Path.Combine(Environment.CurrentDirectory, $"Bhatti{i}.png");
Bitmap bitmap = new Bitmap(image);
bitmap.Save(imagePath);
Hassan Rahman
fonte
0

Para mim, eu estava usando o Image.Save(Stream, ImageCodecInfo, EncoderParameters)e, aparentemente, isso estava causando o A generic error occurred in GDI+erro infame .

Eu estava tentando usar EncoderParameterpara salvar os jpegs em 100% de qualidade. Isso estava funcionando perfeitamente na "minha máquina" (doh!) E não na produção.

Quando usei o Image.Save(Stream, ImageFormat)lugar, o erro desapareceu! Assim, como um idiota, continuei a usar o último, embora ele os salve na qualidade padrão, que eu presumo ser apenas 50%.

Espero que esta informação ajude alguém.

Ε Г И І И О
fonte
0

Eu também encontrei o problema. O problema ocorreu devido ao descarte do fluxo de carregamento. Mas eu não a descartei, estava dentro da estrutura .Net. Tudo o que eu precisava fazer era usar:

image_instance = Image.FromFile(file_name);

ao invés de

image_instance.Load(file_name);

image_instance é do tipo System.Windows.Forms.PictureBox! O PictureBox's Load () descarta o fluxo do qual a imagem foi carregada, e eu não sabia disso.

Klaus
fonte
0

Com base na resposta de @savindra, se você RHM no seu aplicativo e tentar executar como administrador , deverá resolver o seu problema.

O meu parecia ser um problema de permissão.

AltF4_
fonte
0

Os possíveis problemas que causam esse erro são:

  1. O diretório não existe (o método que você está chamando não criará automaticamente esse diretório para você)
  2. As permissões de segurança para gravar no diretório de saída não permitem que o usuário executando o aplicativo grave

Espero que isso ajude, essa foi a correção do meu problema. Simplesmente verifiquei se o diretório de saída existe antes de salvar a imagem de saída!

Ihab Hajj
fonte