Si è verificato un errore generico in GDI +, Immagine JPEG su MemoryStream


326

Questo sembra essere un po 'un famigerato errore in tutto il web. Tanto che non sono stato in grado di trovare una risposta al mio problema poiché il mio scenario non si adatta. Viene generata un'eccezione quando si salva l'immagine nel flusso.

Stranamente funziona perfettamente con un png ma dà l'errore sopra riportato con jpg e gif che è piuttosto confuso.

Il problema più simile là fuori riguarda il salvataggio di immagini in file senza autorizzazioni. Ironia della sorte, la soluzione è utilizzare un flusso di memoria come sto facendo ....

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();
    }
}

Maggiori dettagli all'eccezione. Il motivo per cui ciò causa così tanti problemi è la mancanza di spiegazioni :(

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 cose che ho provato finora.

  1. Clonare l'immagine e lavorarci su.
  2. Recupero dell'encoder per quel MIME passando quello con l'impostazione della qualità jpeg.



3
Per me, il problema era che la cartella non esisteva. Risolto semplicemente creando la cartella.
Hazjack,

Per me era un indice fuori portata che veniva ingoiato.
Billy Jake O'Connor,

Risposte:


189

OK, sembra che abbia trovato la causa solo per pura fortuna e non c'è nulla di sbagliato in quel particolare metodo, è ulteriormente il backup dello stack di chiamate.

In precedenza ho ridimensionato l'immagine e come parte di quel metodo restituisco l'oggetto ridimensionato come segue. Ho inserito due chiamate al metodo sopra e un salvataggio diretto in un file.

// 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;
 }

Sembra che il flusso di memoria su cui è stato creato l'oggetto debba essere aperto nel momento in cui l'oggetto viene salvato. Non sono sicuro del perché. Qualcuno è in grado di illuminarmi e come posso aggirare questo.

Ritorno solo da uno stream perché dopo aver usato il codice di ridimensionamento simile a questo il file di destinazione ha un tipo mime sconosciuto (img.RawFormat.Guid) e Id come il tipo Mime per essere corretti su tutti gli oggetti immagine in quanto rende difficile la scrittura generica gestione del codice in caso contrario.

MODIFICARE

Questo non è emerso nella mia ricerca iniziale, ma ecco la risposta di Jon Skeet


4
Non mi rendevo conto che quando si ottiene una bitmap da un flusso di memoria non è necessario chiudere il flusso. molto utile, grazie
mcdon

38
Grazie. Questo probabilmente ha salvato l'ultimo dei miei capelli.
NotMe

6
Grazie! questo mi ha fatto risparmiare un sacco di tempo, una cosa, ti dispiacerebbe evidenziare la causa dell'errore all'inizio della tua risposta, dato che io (e immagino che la maggior parte dei falchi) l'ho persa sulla scrematura originale attraverso le risposte, forse qualcosa del tipo " NON CHIUDERE LO STREAM DI MEMORIA SE INTENDI USARE DI NUOVO L'IMMAGINE "sarebbe fantastico; D
DorD

6
Qual è la tua variabile "dst"?
WEFX

1
@madcapnmckay, per favore, spiega cos'è la variabile 'dst' e il suo significato
Mike T

131

Se ricevi questo errore, posso dire che la tua applicazione non ha un permesso di scrittura su alcune directory.

Ad esempio, se si sta tentando di salvare l'immagine dal flusso di memoria nel file system, è possibile che si verifichi tale errore.

Per favore, se stai usando XP, assicurati di aggiungere l'autorizzazione di scrittura per l'account aspnet su quella cartella.

Se si utilizza Windows Server (2003, 2008) o Vista, assicurarsi di aggiungere l'autorizzazione di scrittura per l'account del servizio di rete.

Spero che aiuti qualcuno.


7
Non l'hai fatto! Ho perso 2 ore con i dannati permessi di scrittura ... Sono venuto qui per pubblicare questo. Spero che tu ottenga più voti. :)
Gleno,

2
QUESTA era la soluzione per me. +1 totalmente!
Grandizer,

5
Puoi fare File.WriteAllText ("nomefile.jpg", "") quindi File.DeleteFile ("nomefile.jpg") prima di salvare la bitmap. Nel mio benmark ci vogliono solo 0,001 secondi e ottieni un bel 'Non hai i permessi per salvare il nomefile.jpg'
Despertar

@Despertar Intendi File.Delete (), ma è un trucco molto utile! Sicuramente lo userò ogni volta che salverò una bitmap.
D Coetzee,

2
Nel mio caso, la directory non esisteva.
silenziato,

54

Aggiungerò anche questa causa dell'errore nella speranza che possa aiutare qualche futuro viaggiatore su Internet. :)

GDI + limita l'altezza massima di un'immagine a 65500

Facciamo un ridimensionamento di base dell'immagine, ma nel ridimensionamento cerchiamo di mantenere le proporzioni. Abbiamo un addetto al controllo qualità che è un po 'troppo bravo in questo lavoro; decise di testarlo con una foto di UN pixel di larghezza che era alta 480 pixel. Quando l'immagine è stata ridimensionata per soddisfare le nostre dimensioni, l'altezza era a nord di 68.000 pixel e la nostra app è esplosa A generic error occurred in GDI+.

Puoi verificarlo tu stesso con test:

  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+."
  }

Peccato che non ci sia un .net amichevole ArgumentExceptionlanciato nel costruttore di Bitmap.


17
Grazie - questo viaggiatore nel tempo di Internet ti è abbastanza grato per aver lasciato questo messaggio.
Tom West,

Dal mio test, 65535 è in realtà il valore massimo. A 65536 inizio a vedere l'errore generico.
ChaseMedallion

Ho appena provato di nuovo: Win10 .net 4.5 e .net 4.6.1, ed è saltato in aria a 65501 che sembra ancora più casuale. Il codice è anche pieno di errori di sintassi, verrà aggiornato :)
Fred

37

Questo articolo spiega in dettaglio cosa succede esattamente: dipendenze del costruttore di bitmap e immagini

In breve, per una vita di un Imagecostruito da un flusso , il flusso non deve essere distrutto.

Quindi, invece di

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

prova questo

Stream imageStream;
...

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

e chiudi imageStream alla chiusura del modulo o alla chiusura della pagina web.


Sì, questo mi ha preso. Ero coscienzioso e ho avvolto il mio flusso in un usinge successivamente ho provato a copiare l'immagine in un flusso di memoria e ho ricevuto il terribile messaggio "Errore generico in GDI +".
Will Appleby

Il tuo link mi stava dando infiniti reindirizzamenti; questo funziona. Stavo riscontrando un problema con il salvataggio PixelFormat.Format32bppArgbma non PixelFormat.Format1bppIndexed. L'articolo che hai collegato spiega perché: GDI + può scegliere di decodificare nuovamente i dati bitmap dal flusso di origine anziché mantenere tutto in memoria. La mia ipotesi è che non ricodificherà le immagini 1bpp.
labreuer,

Anche il nuovo link non funziona più. Una semplice ricerca su Google non sembra rivelare la pagina corretta. Ma sono stato molto contento di trovare questa risposta! La mia soluzione
alternativa

28

Otterrai anche questa eccezione se provi a salvare in un percorso non valido o se c'è un problema di autorizzazioni.

Se non sei sicuro al 100% che il percorso del file sia disponibile e le autorizzazioni siano corrette, prova a scrivere un file di testo. Ci vogliono solo pochi secondi per escludere quale sarebbe una soluzione molto semplice.

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

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

E non dimenticare di ripulire il tuo file.


Questo è stato il problema per me ... Vorrei che l'errore fosse meno vago, mi avrebbe risparmiato un sacco di tempo.
Oofpez,

Sì! La cartella in cui stai salvando deve esistere. Ora controllo prima quello, prima di provare a salvare un'immagine. (Tuttavia, l'errore mi sorprende, circa una volta all'anno.)
Magnus Smith, il

Il mio percorso era una directory, anziché un file.
Asen Kasimov,

20

Salva immagine nella variabile bitmap

using (var ms = new MemoryStream())
{
    Bitmap bmp = new Bitmap(imageToConvert);
    bmp.Save(ms, format);
    return ms.ToArray();
}

Questo ha risolto il mio problema. Potresti spiegare perché il salvataggio dell'immagine in Bitmap spaventa l'eccezione?
jmc,

Mi ha salvato la giornata .. non so cosa abbia causato il problema, ma il salvataggio di Bitmap funziona .. System.Drawing.Image non verrà salvato nel flusso di memoria, ma Bitmap lo fa !!!
San

Questa è stata la soluzione migliore per me. Creazione di una nuova bitmap e conversione da essa.
uzay95,

17

Nel caso in cui qualcuno stia facendo cose stupide come me. 1. assicurati che il percorso esista. 2. assicurati di avere i permessi per scrivere. 3. assicurati che il tuo percorso sia corretto, nel mio caso mi mancava il nome del file in TargetPath :(

avrebbe dovuto dire, il tuo percorso fa schifo di "Si è verificato un errore generico in GDI +"


16

Ho anche riscontrato questo errore durante il salvataggio di JPEG, ma solo per alcune immagini.

Il mio codice finale:

  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
  }

Non ho creato le immagini, quindi non so quale sia la differenza.
Gradirei se qualcuno potesse spiegarlo.

Questa è la mia funzione SaveJpeg solo 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;
}

1
Questo risolto giorni di tirare i capelli. È il codice più wtf che penso di aver mai scritto :)
Jeff Dunlop,

13

Ho scoperto che se una delle cartelle principali in cui stavo salvando il file avesse uno spazio finale, GDI + genererebbe l'eccezione generica.

In altre parole, se ho provato a salvare in "C: \ Documents and Settings \ nomeutente \ Impostazioni locali \ Temp \ ABC DEF M1 Valori di tendenza \ Immagini \ picture.png", viene generata l'eccezione generica.

Il nome della mia cartella veniva generato da un nome file che aveva uno spazio finale, quindi era facile .Trim () e andare avanti.


3
fantastico - non avrei mai pensato di guardare il percorso della directory così da vicino
jharr100

11

se il codice è il seguente, si verifica anche questo errore

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

Quello corretto è

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

Ciò può essere dovuto al fatto che stiamo tornando dal blocco using


per me è stato il ritorno nel blocco using. Uso ancora usando ma restituisco il valore al di fuori del blocco. Grazie!
Dragouf,

1
Ho scoperto "nel modo più duro" che se stai salvando di nuovo quell'immagine su un nuovo stream (come HttpContext.Response.OutputStream per esempio) dovrai anche fare uno stream.Flush (), se non si verifica l'errore ancora.
Lucian,

11

Questa è un'espansione / qualificazione della risposta di Fred che affermava: "GDI limita l'altezza di un'immagine a 65534". Abbiamo riscontrato questo problema con una delle nostre applicazioni .NET e, dopo aver visto il post, il nostro team di outsourcing ha alzato le mani in aria e ha detto che non potevano risolvere il problema senza grandi cambiamenti.

Sulla base dei miei test, è possibile creare / manipolare immagini con un'altezza maggiore di 65534, ma il problema si presenta quando si salva su uno stream o un file IN ALCUNI FORMATI . Nel codice seguente, la chiamata del metodo t.Save () genera al nostro amico l'eccezione generica quando l'altezza dei pixel è 65501 per me. Per motivi di curiosità, ho ripetuto il test per la larghezza e lo stesso limite si applicava al salvataggio.

    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();
    }

Lo stesso errore si verifica anche se si scrive su un flusso di memoria.

Per aggirare il problema, puoi ripetere il codice sopra e sostituire ImageFormat.Tiff o ImageFormat.Bmp per ImageFormat.Jpeg.

Questo per me raggiunge altezze / larghezze di 100.000 - Non ho testato i limiti. In effetti, Tiff è stata un'opzione praticabile per noi.

ATTENZIONE

I flussi / file TIFF in memoria consumano più memoria rispetto alle loro controparti JPG.


10

Ha avuto un problema molto simile e ha anche provato a clonare l'immagine che non funziona. Ho scoperto che la soluzione migliore era quella di creare un nuovo oggetto Bitmap dall'immagine caricata dal flusso di memoria. In questo modo il flusso può essere eliminato ad es

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

Spero che questo ti aiuti.


6

Errore durante l'autorizzazione. assicurati che la cartella abbia TUTTO IL PERMESSO.

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")

Sono d'accordo con te. Ho risolto questo problema con
PERMISSION

5

RISOLTO - Ho avuto questo esatto problema. La soluzione, per me, era aumentare la quota del disco per IUSR sul server IIS. In questo caso, abbiamo un'app di catalogo con immagini di articoli e simili. La quota di caricamento per "Utente Web anonimo" era impostata su 100 MB, che è l'impostazione predefinita per i server IIS di questa particolare società di hosting. L'ho aumentato a 400 MB ed è stato in grado di caricare immagini senza errori.

Questo potrebbe non essere il tuo problema, ma se lo è, è una soluzione semplice.


4

Nel mio caso il problema era nel percorso che stavo salvando (il root C:\). Modificandolo per far D:\111\scomparire l'eccezione.


4

Un'altra causa di questo errore: il percorso indicato nel metodo Save dell'istanza Bitmap non esiste o non è stato fornito un percorso completo / valido.

Ho appena avuto questo errore perché stavo passando un nome file e non un percorso completo!

Succede!


4

Il mio turno!

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

Ottenuto sul .Save ... perché using () tiene aperto il file, quindi non posso sovrascriverlo. Forse questo aiuterà qualcuno in futuro.


4

Lo stesso problema che stavo affrontando. Ma nel mio caso, stavo cercando di salvare il file nell'unità C e non era accessibile. Così ho provato a salvare nel disco D che era completamente accessibile e ci sono riuscito.

Quindi prima controlla le cartelle in cui stai tentando di salvare. È necessario disporre di tutti i diritti (di lettura e scrittura) per quella particolare cartella.


causa normalmente c non consente senza l'autorizzazione dell'amministratore.
Aneeq Azam Khan,

2

Ho notato che il tuo caso "jpeg" è in realtà:

            default:
                format = ImageFormat.Jpeg;
                break;

Sei sicuro che il formato sia jpeg e non qualcos'altro?

Proverei:

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

O controlla cosa imageToConvert.MimeType()sta effettivamente tornando.

AGGIORNARE

C'è qualche altra inizializzazione che devi fare all'oggetto MemoryStream?


Grazie. Viene sicuramente chiamato con il formato corretto. Carico un jpg, eseguo il debug e confermo che il mime è riconosciuto come immagine / jpeg e il formato è JPG.
madcapnmckay,

3
Vabbè, cerco sempre di eliminare prima l'ovvio. Non riesco a contare il numero di volte che non l'ho fatto ed è tornato a mordermi più tardi.
ChrisF

2
  • Ho riscontrato questo problema su un server di prova ma non sul server live.
  • Stavo scrivendo l'immagine su uno stream, quindi non era un problema di autorizzazione.
  • Avevo distribuito direttamente alcuni file DLL sul server di test.
  • La distribuzione dell'intera soluzione ha risolto il problema, quindi probabilmente era una strana discrepanza nella compilazione

2

Solo per lanciare un'altra possibile soluzione sul mucchio, menzionerò il caso in cui mi sono imbattuto in questo messaggio di errore. Il metodo Bitmap.Savegenererebbe questa eccezione durante il salvataggio di una bitmap che avevo trasformato e che stavo visualizzando. Ho scoperto che non genererebbe l'eccezione se l'affermazione avesse un punto di interruzione su di essa, né lo farebbe se Bitmap.Savefosse preceduta daThread.Sleep(500) così suppongo che ci sia una sorta di contesa di risorse in corso.

Copiare semplicemente l'immagine su un nuovo oggetto Bitmap è stato sufficiente per impedire la visualizzazione di questa eccezione:

new Bitmap(oldbitmap).Save(filename);

2

Abbiamo riscontrato un problema simile nel generare un'immagine PDFo ridimensionare utilizzando la libreria ImageProcessor sul server di produzione.

Riciclare il pool di applicazioni per risolvere il problema.


1

Se si sta tentando di salvare un'immagine in una posizione remota, assicurarsi di aggiungere l' NETWORK_SERVICEaccount utente nelle impostazioni di sicurezza e concedere a tale utente le autorizzazioni di lettura e scrittura. Altrimenti non funzionerà.


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);
}


1

Semplice, creare una nuova istanza di Bitmap risolve il problema.

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

0

Per me stavo usando Image.Save(Stream, ImageCodecInfo, EncoderParameters)e apparentemente questo stava causando il famigerato A generic error occurred in GDI+errore.

Stavo cercando di utilizzare EncoderParameterper salvare i jpeg in qualità al 100%. Funzionava perfettamente sulla "mia macchina" (doh!) E non sulla produzione.

Quando ho usato Image.Save(Stream, ImageFormat)invece, l'errore è scomparso! Quindi, come un idiota, ho continuato a utilizzare quest'ultimo, anche se li salva in qualità predefinita che presumo sia solo del 50%.

Spero che queste informazioni aiutino qualcuno.


0

Ho riscontrato anche il problema. Il problema era dovuto allo smaltimento del flusso di caricamento. Ma non l'ho smaltito, era all'interno del framework .Net. Tutto quello che dovevo fare era usare:

image_instance = Image.FromFile(file_name);

invece di

image_instance.Load(file_name);

image_instance è di tipo System.Windows.Forms.PictureBox! Load () di PictureBox elimina il flusso da cui è stata caricata l'immagine e non lo sapevo.


0

Sulla base della risposta di @savindra, se esegui il RHM sulla tua applicazione e provi ad eseguire come amministratore , il problema dovrebbe essere risolto.

Il mio sembrava essere un problema di autorizzazione.


0

I possibili problemi che causano tale errore sono:

  1. La directory non esiste (il metodo che stai chiamando non creerà automaticamente questa directory per te)
  2. Le autorizzazioni di sicurezza per scrivere nella directory di output non consentono all'utente che esegue l'app di scrivere

Spero che questo aiuti, questa è stata la correzione del mio problema, ho semplicemente fatto in modo che la directory di output esistesse prima di salvare l'immagine di output!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.