364 votes

Une erreur générique s'est produite dans GDI+, Image JPEG vers MemoryStream

Il semble que ce soit une erreur tristement célèbre sur le web. A tel point que je n'ai pas pu trouver de réponse à mon problème car mon scénario ne correspond pas. Une exception est levée lorsque je sauvegarde l'image dans le flux.

Bizarrement, cela fonctionne parfaitement avec un png mais donne l'erreur ci-dessus avec un jpg et un gif, ce qui est plutôt déroutant.

La plupart des problèmes similaires concernent l'enregistrement d'images dans des fichiers sans autorisation. Ironiquement, la solution consiste à utiliser un flux de mémoire comme je le fais.....

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

Plus de détails sur l'exception. La raison pour laquelle cela cause tant de problèmes est le manque d'explication :(

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 les choses que j'ai essayé jusqu'à présent.

  1. Je clone l'image et je travaille là-dessus.
  2. Récupérer l'encodeur pour ce MIME en le passant avec le paramètre de qualité jpeg.

Quelqu'un peut-il m'aider ?

0 votes

0 votes

0 votes

Pour moi, c'est un indice hors norme qui a été avalé.

222voto

madcapnmckay Points 8477

OK, il semble que j'ai trouvé la cause par pure chance et il n'y a aucun problème avec cette méthode particulière, c'est plus loin dans la pile d'appels.

Auparavant, je redimensionne l'image et dans le cadre de cette méthode, je renvoie l'objet redimensionné comme suit. J'ai inséré deux appels à la méthode ci-dessus et une sauvegarde directe dans un fichier.

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

Il semble que le flux de mémoire sur lequel l'objet a été créé a pour être ouvert au moment où l'objet est sauvegardé. Je ne sais pas pourquoi. Quelqu'un peut-il m'éclairer et me dire comment contourner ce problème ?

Je ne retourne qu'à partir d'un flux car après avoir utilisé le code de redimensionnement similaire à ce le fichier de destination a un type de mime inconnu (img.RawFormat.Guid) et j'aimerais que le type de mime soit correct pour tous les objets image car sinon, il est difficile d'écrire du code de traitement générique.

EDIT

Cela n'est pas apparu dans ma recherche initiale mais voici la réponse de Jon Skeet

6 votes

Je n'avais pas réalisé que lorsque vous obtenez un bitmap à partir d'un flux de mémoire, vous ne devez pas fermer le flux. Très utile, merci.

49 votes

Merci. Cela a probablement sauvé le dernier de mes cheveux.

7 votes

Merci ! Cela m'a fait gagner beaucoup de temps, une chose cependant, pourriez-vous mettre en évidence la cause de l'erreur au début de votre réponse car je (et je suppose que la plupart des autres) l'ont manqué lors du premier survol des réponses, peut-être quelque chose comme "Ne fermez pas le flux de mémoire si vous avez l'intention d'utiliser l'image à nouveau" serait génial ;D

148voto

Savindra Points 421

Si vous obtenez cette erreur, alors je peux dire que votre application n'a pas la permission d'écrire sur un répertoire.

Par exemple, si vous essayez d'enregistrer l'image du flux de mémoire vers le système de fichiers, vous pouvez obtenir cette erreur.

Si vous utilisez XP, assurez-vous d'ajouter la permission d'écriture pour le compte aspnet sur ce dossier.

Si vous utilisez Windows Server (2003, 2008) ou Vista, assurez-vous d'ajouter une autorisation d'écriture pour le compte de service réseau.

J'espère que cela aidera quelqu'un.

9 votes

Tu ne l'as pas fait ! J'ai perdu 2 heures avec ces fichues autorisations d'écriture... Je suis venu ici pour poster ça. J'espère que tu auras plus de votes positifs. :)

2 votes

C'était la solution pour moi. +1 totalement !

5 votes

Vous pouvez faire File.WriteAllText("nomfichier.jpg", "") puis File.DeleteFile("nomfichier.jpg") avant d'enregistrer le bitmap. Dans mon cas, cela ne prend que 0,001 seconde et vous obtenez un joli message "Vous n'avez pas l'autorisation d'enregistrer le nom du fichier.jpg à cet endroit".

64voto

Fred Points 457

Pour tous les futurs tireurs de cheveux, parce que "l'erreur générique" est super utile, j'ajouterai également la cause de l'erreur en espérant que cela aidera un futur voyageur Internet :)

Nous effectuons un redimensionnement basique des images, mais en redimensionnant nous essayons de maintenir le rapport d'aspect. Comme nous avons un responsable de l'assurance qualité qui est un peu trop doué pour ce travail, il a décidé de faire un test avec une photo de 480 pixels de haut et de 1 pixel de large. Lorsque l'image a été mise à l'échelle pour répondre à nos dimensions (nous ajoutons un en-tête en haut), la hauteur était supérieure à 68 000 pixels.

GDI+ limite la hauteur d'une image à 65534

Vous pouvez le vérifier vous-même par un simple test de code :

int width = 480;
int height = Int16.MaxValue;
try
{
    while(Height <= Int32.MaxValue)
    {
        Image image = new Bitmap(width, height);
        using(MemoryStream ms = new MemoryStream())
        {
            //error will throw from here
            image.Save(ms, ImageFormat.Jpeg);
        }
        height += 100;
        if(height < Int16.MaxValue)
        {
        }
    }
}
catch(Exception ex)
{
    //explodes at 65567 with "A generic error occurred in GDI+."
}

C'est dommage qu'il n'y ait pas une exception .net Argument amicale lancée dans le constructeur de Bitmap.

22 votes

Merci - ce voyageur du temps sur Internet vous est très reconnaissant d'avoir laissé ce message.

0 votes

D'après mes tests, 65535 est en fait la valeur maximale. À 65536, je commence à voir l'erreur générique.

0 votes

Je viens de réessayer : Win10 .net 4.5 et .net 4.6.1, et il a explosé à 65501 qui semble encore plus aléatoire. Le code est également truffé d'erreurs de syntaxe, je mettrai à jour :)

40voto

Ivan Mesic Points 301

Cet article explique en détail ce qui se passe exactement : Dépendances des constructeurs Bitmap et Image

En bref, pour une vie entière d'un Image construit à partir d'un flux le flux ne doit pas être détruit.

Donc, au lieu de

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

essayez ceci

Stream imageStream;
...

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

et fermer imageStream à la fermeture du formulaire ou de la page web.

0 votes

Ouais, celui-là m'a eu. J'étais consciencieux et j'ai enveloppé mon courant dans un using J'ai ensuite essayé de copier l'image dans un flux de mémoire et j'ai reçu le redoutable message "Generic error in GDI+".

0 votes

Votre lien me donnait des redirections infinies ; celui-ci fonctionne. J'avais un problème avec la sauvegarde PixelFormat.Format32bppArgb mais pas PixelFormat.Format1bppIndexed . L'article que vous avez cité explique pourquoi : GDI+ peut choisir de redécoder les données bitmap à partir du flux source plutôt que de tout garder en mémoire. Je pense qu'il ne redécode pas les images 1bpp.

0 votes

Même le nouveau lien ne fonctionne plus. Une simple recherche sur Google n'a pas semblé révéler la bonne page. Mais j'ai été très heureux de trouver cette réponse ! Ma solution de contournement en copiant vers un nouveau bitmap a échoué en raison des éléments suivants este tema

32voto

Kirk Broadhurst Points 13093

Vous obtiendrez également cette exception si vous essayez d'enregistrer dans un chemin non valide ou s'il y a un problème d'autorisation.

Si vous n'êtes pas sûr à 100% que le chemin d'accès au fichier est disponible et que les autorisations sont correctes, essayez d'écrire dans un fichier texte. Cela ne prend que quelques secondes pour écarter ce qui pourrait être une solution très simple.

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

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

Et n'oubliez pas de nettoyer votre dossier.

0 votes

C'était le problème pour moi... J'aurais aimé que l'erreur soit moins vague, cela m'aurait fait gagner beaucoup de temps.

0 votes

Oui ! Le dossier dans lequel vous enregistrez doit exister. Je le vérifie maintenant avant d'essayer d'enregistrer une image. (Pourtant, l'erreur me surprend, environ une fois par an).

0 votes

Mon chemin était un répertoire, au lieu d'un fichier.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X