web-dev-qa-db-fra.com

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

Cela semble être un peu une erreur infâme sur le web. À tel point que je n’ai pas trouvé de réponse à mon problème car mon scénario ne me convient 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 jpg et gif qui est plutôt déroutant.

La plupart des problèmes similaires concernent la sauvegarde d’images dans des fichiers sans autorisations. 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 à 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ées jusqu'à présent.

  1. Cloner l'image et y travailler.
  2. Récupérer l'encodeur pour ce MIME en le transmettant avec le paramètre de qualité jpeg.
307
madcapnmckay

OK, il me semble que la cause a été trouvée par pure chance et que sa méthode n’a rien d’inconvénient, c’est une sauvegarde supplémentaire de la pile d’appels.

Auparavant, je redimensionnais l'image et dans le cadre de cette méthode, je retournais 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éé doit être ouvert au moment de la sauvegarde de l'objet. Je ne sais pas pourquoi c'est. Est-ce que n'importe qui peut m'éclairer et comment je peux contourner ceci.

Je ne reviens que d'un flux, car après avoir utilisé le code de redimensionnement similaire à this , le fichier de destination a un type mime inconnu (img.RawFormat.Guid) et un ID identique au type Mime pour être correct sur tous les objets d'image. sinon, il est difficile d'écrire du code de traitement générique.

EDIT

Cela n'a pas été abordé dans ma recherche initiale mais voici la réponse de Jon Skeet

174
madcapnmckay

Si vous obtenez cette erreur, alors je peux dire que votre application n'a pas de permission d'écriture sur certains répertoires.

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

S'il vous plaît, si vous utilisez XP, assurez-vous d'ajouter l'autorisation d'écriture pour le compte aspnet sur ce dossier.

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

J'espère que ça aidera quelqu'un.

122
Savindra

J'ajouterai cette cause de l'erreur en espérant que cela aidera un futur voyageur Internet. :)

GDI + limite la hauteur maximale d'une image à 655

Nous faisons quelques redimensionnements d'image de base, mais dans le redimensionnement, nous essayons de conserver les proportions. Nous avons un gars d'AQ qui est un peu trop bon à ce travail; il a décidé de le tester avec une photo de 1 pixel de large, d'une hauteur de 480 pixels. Lorsque l'image a été adaptée à nos dimensions, la hauteur était de 68 000 pixels au nord et notre application a explosé avec A generic error occurred in GDI+.

Vous pouvez le vérifier vous-même avec le 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+."
  }

C'est dommage qu'il n'y ait pas de .net ArgumentException amical jeté dans le constructeur de Bitmap.

51
Fred

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

En bref, pendant toute la durée de vie 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);
}

essaye ça

Stream imageStream;
...

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

et fermez imageStream à la fermeture du formulaire ou de la page Web.

37
Ivan Mesic

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

Si vous n'êtes pas sûr à 100% que le chemin du fichier est disponible et que les autorisations sont correctes, essayez d'écrire un fichier dans un fichier texte. Cela ne prend que quelques secondes pour exclure 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 fichier.

28
Kirk Broadhurst

Enregistrer l'image en variable bitmap

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

Juste au cas où quelqu'un ferait des choses aussi stupides que moi. 1. assurez-vous que le chemin existe. 2. assurez-vous que vous avez les autorisations nécessaires pour écrire. 3. assurez-vous que votre chemin est correct, dans mon cas, il me manquait le nom de fichier dans le chemin cible :(

il aurait dû dire, votre chemin est nul "qu'une erreur générique s'est produite dans GDI +"

14
ahsant

J'ai aussi eu cette erreur lors de l'enregistrement de fichiers JPEG, mais uniquement pour certaines images.

Mon code 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
  }

Je n'ai pas créé les images, donc je ne peux pas dire quelle est la différence.
Je vous serais reconnaissant si quelqu'un pouvait expliquer cela.

Ceci est ma fonction SaveJpeg juste pour info:

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;
}
14
Aximili

J'ai constaté que si l'un des dossiers parents dans lequel je sauvegardais le fichier avait un espace de fin, GDI + lançait l'exception générique.

En d’autres termes, si j’essayais de sauvegarder dans "C:\Documents and Settings\myusername\Local Settings\Temp\ABC DEF M1 Trended Values ​​\ Images\picture.png", une exception générique était lancée.

Mon nom de dossier était généré à partir d'un nom de fichier qui comportait un espace de fin. Il était donc facile de le faire .Trim () et de continuer.

13
Igilima

C'est une extension/qualification de la réponse de Fred qui déclarait: "GDI limite la hauteur d'une image à 65534". Nous avons rencontré ce problème avec l'une de nos applications .NET et, après avoir vu le message, notre équipe de sous-traitance a levé la main en l'air et a déclaré qu'elle ne pourrait pas résoudre le problème sans changements majeurs.

D'après mes tests, il est possible de créer/manipuler des images d'une hauteur supérieure à 65534, mais le problème se pose lors de l'enregistrement dans un flux ou un fichier IN CERTAIN FORMATS. Dans le code suivant, l'appel à la méthode t.Save () lève l'exception générique à notre ami lorsque la hauteur en pixels est de 65501 pour moi. Pour des raisons de curiosité, j'ai répété le test de largeur et la même limite s'appliquait à la sauvegarde.

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

La même erreur se produit également si vous écrivez dans un flux de mémoire.

Pour le contourner, vous pouvez répéter le code ci-dessus et remplacer ImageFormat.Tiff ou ImageFormat.Bmp par ImageFormat.Jpeg.

Cela atteint des hauteurs/largeurs de 100 000 pour moi - je n'ai pas testé les limites. Il se trouve que Tiff était une option viable pour nous.

soyez averti

Les flux/fichiers TIFF en mémoire consomment plus de mémoire que leurs homologues JPG.

11
vipes

si votre code est comme suit, alors aussi cette erreur se produit

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

Le bon est

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

C'est peut-être parce que nous revenons du bloc using

10
dhinesh

Avait un problème très similaire et a également essayé de cloner l'image qui ne fonctionne pas. J'ai trouvé que la meilleure solution consistait à créer un nouvel objet Bitmap à partir de l'image chargée à partir du flux de mémoire. De cette façon, le flux peut être éliminé, par exemple.

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

J'espère que cela t'aides.

9
HU.

Erreur due à une permission. assurez-vous que le dossier a TOUTE LA PERMISSION.

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")
5
vivek shaushi

Résolu - J'ai eu ce problème exact. Pour moi, le correctif consistait à augmenter le quota de disque pour IUSR sur le serveur IIS. Dans ce cas, nous avons une application de catalogue avec des images d'éléments et autres. Le quota de téléchargement pour "l'utilisateur Web anonyme" a été défini sur 100 Mo, valeur par défaut pour les serveurs IIS de cette société d'hébergement. Je l'ai porté à 400 Mo et j'ai pu télécharger des images sans erreur.

Ce n'est peut-être pas votre problème, mais si c'est le cas, c'est une solution facile.

5
Marco

Même problème que je rencontrais. Mais dans mon cas, j'essayais de sauvegarder le fichier dans le lecteur C et il n'était pas accessible. J'ai donc essayé de sauvegarder dans le lecteur D qui était entièrement accessible et j'ai réussi.

Commencez donc par vérifier vos dossiers dans lesquels vous essayez de sauvegarder. Vous devez disposer de tous les droits (lecture et écriture) pour ce dossier particulier.

4
Jaimin

Dans mon cas, le problème était dans le chemin que je sauvegardais (la racine C:\). Le changer en D:\111\ fit disparaître l'exception.

4
Ani

Une autre cause de cette erreur - le chemin que vous indiquez dans la méthode Save de l'occurrence Bitmap n'existe pas ou vous n'avez pas fourni de chemin complet/valide.

Je viens de recevoir cette erreur parce que je passais un nom de fichier et non un chemin complet!

Ça arrive!

4
MytyMyky

Mon tour!

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

Je l'ai sur le .Save ... parce que using () tient le fichier ouvert, je ne peux donc pas l'écraser. Peut-être que cela aidera quelqu'un dans le futur.

4
Andy

Juste pour jeter une autre solution possible sur la pile, je vais mentionner le cas que j'ai rencontré avec ce message d'erreur. La méthode Bitmap.Save lève cette exception lors de l'enregistrement d'un bitmap que j'ai transformé et que je suis en train d'afficher. J'ai découvert que l'exception ne serait pas levée si l'instruction contenait un point d'arrêt, pas plus que si le Bitmap.Save était précédé de Thread.Sleep(500), je suppose donc qu'il existe une sorte de conflit de ressources.

Copier simplement l'image dans un nouvel objet Bitmap suffisait à empêcher cette exception d'apparaître:

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

Je remarque que votre cas "jpeg" est en réalité:

            default:
                format = ImageFormat.Jpeg;
                break;

Êtes-vous sûr que le format est jpeg et pas autre chose?

J'essaierais:

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

Ou vérifiez ce que imageToConvert.MimeType() est en train de retourner.

UPDATE

Y a-t-il une autre initialisation à faire avec l'objet MemoryStream?

2
ChrisF
  • J'ai eu ce problème sur un serveur de test, mais pas sur le serveur live.
  • J'écrivais l'image dans un flux, ce n'était donc pas un problème d'autorisation.
  • J'avais déployé directement certains fichiers .dll sur le serveur de test.
  • Le déploiement de la solution complète a résolu le problème. Il s'agissait donc probablement d'un problème de compilation étrange.
2
Chris Halcrow

Je reçois aussi cette erreur parce que j'essaie de sauvegarder des images avec le même nom que les images enregistrées précédemment.

Assurez-vous de ne pas enregistrer les images avec un nom en double.

Utilisez, par exemple, une fonction 'Random' ( Comment fonctionne le générateur de nombres aléatoires de C #? ) ou générez par exemple un Guid ( http://betterexplained.com/articles/the- guide rapide des guides / )

1
Bruno Ferreira
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
jeka

Si vous essayez d'enregistrer une image sur un emplacement distant, veillez à bien ajouter le compte utilisateur NETWORK_SERVICE dans les paramètres de sécurité et à lui donner les autorisations de lecture et d'écriture. Sinon, ça ne va pas marcher.

1
JAH

nous avons eu un peu le même problème pour générer un PDF sur un serveur de production.

recyclez le pool d'applications résolvez le problème.

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

1
tech-gayan

Mon application de console a reçu le même message d'erreur: "Une erreur générique s'est produite dans GDI +." L'erreur est survenue dans la ligne newImage.Save car référez-vous au code suivant.

for (int i = 1; i <= 1000; i++)
{
   Image newImage = Image.FromFile(@"Sample.tif");
   //...some logic here
   newImage.Save(i + ".tif", , ImageFormat.Tiff);
}

Le programme a renvoyé une erreur lorsque l'utilisation de la RAM est d'environ 4 Go et a réussi à la résoudre en remplaçant cible du programme par x64 dans les propriétés du projet.

0
Soon Khai

Simple, créer une nouvelle instance de Bitmap résout le problème.

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

J'ai rencontré le problème aussi. Le problème était dû au déchargement du flux de chargement. Mais je ne l'ai pas éliminé, c'était à l'intérieur du framework .Net. Tout ce que j'avais à faire était d'utiliser:

image_instance = Image.FromFile(file_name);

au lieu de

image_instance.Load(file_name);

image_instance est de type System.Windows.Forms.PictureBox! Load () de PictureBox dispose du flux à partir duquel l'image a été chargée, ce que je ne savais pas.

0
Klaus

Les problèmes possibles à l'origine d'une telle erreur sont les suivants:

  1. Le répertoire n'existe pas (la méthode que vous appelez ne créera pas automatiquement ce répertoire pour vous)
  2. Les autorisations de sécurité permettant d’écrire dans le répertoire de sortie ne permettent pas à l’utilisateur exécutant l’application d’écrire.

J'espère que cela aide, c'était la solution à mon problème, je me suis simplement assuré que le répertoire de sortie existait avant de sauvegarder l'image de sortie!

0
Ihab Hajj

D'après la réponse de @savindra, si vous RHM sur votre application et que vous essayez de l'exécuter en tant que administrateur, votre problème devrait alors être résolu.

Le mien semblait être un problème de permission.

0
AltF4_

Pour moi, j'utilisais la Image.Save(Stream, ImageCodecInfo, EncoderParameters) et apparemment, cela provoquait la fameuse erreur A generic error occurred in GDI+.

J'essayais d'utiliser EncoderParameter pour enregistrer les fichiers jpeg avec une qualité de 100%. Cela fonctionnait parfaitement sur "ma machine" (doh!) Et non sur la production.

Quand j'ai utilisé le Image.Save(Stream, ImageFormat) à la place, l'erreur a disparu! Donc, comme un idiot, j'ai continué à utiliser ce dernier bien que cela les enregistre en qualité par défaut, ce qui, je suppose, ne représente que 50%.

J'espère que cette information aide quelqu'un.

0
Ε Г И І И О

Une autre cause de cette erreur et qui résout mon problème est que votre application n’est pas autorisée en écriture sur certains répertoires.

donc pour compléter la réponse de savindra: https://stackoverflow.com/a/7426516/6444829 .

Voici comment vous accordez l'accès au fichier à IIS_IUSERS

Pour permettre l'accès à une application ASP.NET, vous devez autoriser l'accès à IIs_IUSERS.

Pour accorder des autorisations de lecture, d'écriture et de modification sur un fichier ou un dossier spécifique

  1. Dans l'Explorateur Windows, recherchez et sélectionnez le fichier requis.

  2. Cliquez avec le bouton droit sur le fichier, puis cliquez sur Propriétés.

  3. Dans la boîte de dialogue Propriétés, cliquez sur l'onglet Sécurité.

  4. Dans l'onglet Sécurité, examinez la liste des utilisateurs. (Si votre application s'exécute en tant que service réseau, ajoutez le compte de service réseau à la liste et accordez-lui l'autorisation.

  5. Dans la boîte de dialogue Propriétés, cliquez sur IIs_IUSERS, puis dans la section Autorisations pour NETWORK SERVICE, sélectionnez les autorisations de lecture, d'écriture et de modification.

  6. Cliquez sur Appliquer, puis sur OK.

cela a fonctionné pour moi dans mon IIS de Windows Server 2016 et dans le local IIS Windows 10.

0
Khalil Liraqui