120 votes

Image.Save(..) lève une exception GDI+ car le flux de mémoire est fermé.

J'ai des données binaires que je veux sauvegarder comme une image. Lorsque j'essaie d'enregistrer l'image, une exception est levée si le flux de mémoire utilisé pour créer l'image a été fermé avant l'enregistrement. La raison pour laquelle je fais cela est que je crée des images de façon dynamique et qu'en tant que tel, je dois utiliser un flux de mémoire.

voici le code :

[TestMethod]
public void TestMethod1()
{
    // Grab the binary data.
    byte[] data = File.ReadAllBytes("Chick.jpg");

    // Read in the data but do not close, before using the stream.
    Stream originalBinaryDataStream = new MemoryStream(data);
    Bitmap image = new Bitmap(originalBinaryDataStream);
    image.Save(@"c:\test.jpg");
    originalBinaryDataStream.Dispose();

    // Now lets use a nice dispose, etc...
    Bitmap2 image2;
    using (Stream originalBinaryDataStream2 = new MemoryStream(data))
    {
        image2 = new Bitmap(originalBinaryDataStream2);
    }

    image2.Save(@"C:\temp\pewpew.jpg"); // This throws the GDI+ exception.
}

Quelqu'un a-t-il une idée de la façon dont je pourrais sauvegarder une image avec le flux fermé ? Je ne peux pas compter sur les développeurs pour se souvenir de fermer le flux après l'enregistrement de l'image. En fait, le développeur n'aurait AUCUNE idée que l'image a été générée en utilisant un flux de mémoire (parce que cela se produit dans un autre code, ailleurs).

Je suis vraiment confus :(

1 votes

J'ai reçu ce commentaire de @HansPassant dans un autre message. question . Vous obtiendrez cette exception chaque fois que le codec aura des difficultés à écrire le fichier. Une bonne instruction de débogage à ajouter est System.IO.File.WriteAllText(path, "test") avant l'appel Save(), elle vérifie la capacité de base à créer le fichier. Vous obtiendrez maintenant une bonne exception qui vous dira ce que vous avez fait de mal.

0 votes

Vous devriez image2.Save à l'intérieur using bloc. Je pense que le originalBinaryDataStream2 a été automatiquement éliminé à la fin de l'utilisation. Et c'est ce qui a déclenché l'exception.

187voto

Jon Skeet Points 692016

Comme il s'agit d'un MemoryStream, vous n'avez pas vraiment besoin de pour fermer le flux - rien de mal ne se passera si vous ne le faites pas, même si c'est une bonne pratique de se débarrasser de tout ce qui est jetable de toute façon. (Voir cette question pour en savoir plus à ce sujet).

Cependant, vous devrait de disposer du Bitmap - et cela fermera le flux pour vous. En fait, dès que vous donnez un flux au constructeur du Bitmap, il "possède" le flux et vous ne devez pas le fermer. Comme les docs pour ce constructeur dites :

Vous devez garder le flux ouvert pendant la vie du Bitmap.

Je n'ai pas trouvé de documentation promettant de fermer le flux lorsque vous disposez du bitmap, mais vous devriez être en mesure de le vérifier assez facilement.

4 votes

Génial ! C'est une excellente réponse Jon. C'est tout à fait logique (et j'ai manqué la partie sur le flux dans la documentation). Deux pouces en l'air ! Je ferai un rapport quand je l'aurai essayé :)

0 votes

Des commentaires sur la façon de procéder si nous voulons respecter la règle CA2000 ? (msdn.microsoft.com/fr/us/library/ms182289.aspx)

1 votes

@Patrick : Ce n'est tout simplement pas applicable - vous avez transféré la propriété de la ressource, fondamentalement. Le plus proche que vous puissiez faire serait de créer un wrapper "NonClosingStream" qui ignore l'appel Dispose. Je pense que j'en ai peut-être un dans MiscUtil - je ne suis pas sûr...

105voto

Houman Points 346

Une erreur générique s'est produite dans GDI+. Peut également être causé par la fourniture chemin de sauvegarde incorrect ! Il m'a fallu une demi-journée pour le remarquer. Donc, assurez-vous que vous avez vérifié le chemin de sauvegarde aussi..,

5 votes

Je suis content d'avoir vu ça, mon chemin était C\Users\mason\Desktop\pic.png . Deux points manquants ! J'aurais passé une éternité avant de le remarquer.

4 votes

Incorrect signifie également que le dossier dans lequel vous souhaitez enregistrer l'image n'existe pas.

14voto

Rojzik Points 763

Il est peut-être utile de mentionner que si le C:\Temp n'existe pas, il lancera également cette exception même si votre flux est toujours existant.

0 votes

+1 Cette exception semble se produire dans une variété de scénarios. Le chemin d'accès invalide est l'un de ceux que j'ai rencontrés aujourd'hui.

4voto

Brian Low Points 3642

Copiez le Bitmap. Vous devez garder le flux ouvert pendant toute la durée de vie du bitmap.

Lors du dessin d'une image : System.Runtime.InteropServices.ExternalException : Une erreur générique s'est produite dans GDI

    public static Image ToImage(this byte[] bytes)
    {
        using (var stream = new MemoryStream(bytes))
        using (var image = Image.FromStream(stream, false, true))
        {
            return new Bitmap(image);
        }
    }

    [Test]
    public void ShouldCreateImageThatCanBeSavedWithoutOpenStream()
    {
        var imageBytes = File.ReadAllBytes("bitmap.bmp");

        var image = imageBytes.ToImage();

        image.Save("output.bmp");
    }

1 votes

Cela ne fonctionne pas exactement ; dans votre code dans ToImage(), l'"image" locale aura correctement un .RawFormat de n'importe quel fichier original (jpeg ou png, etc.), alors que la valeur de retour de ToImage() aura inopinément un .RawFormat MemoryBmp.

0 votes

Je ne sais pas comment le RawFormat n'a pas beaucoup d'importance, cependant. Si vous voulez l'utiliser, récupérez-le à partir de l'objet quelque part en cours de route, mais en général, enregistrez sous le type que vous voulez réellement ont .

0voto

sirrocco Points 4153

Ce que Jon veut dire, c'est qu'après :

using(Stream.....){
}// here the stream is closed so when you try to save - you get an exception

Il serait également utile que vous ne mettiez pas le code dans une image et que vous le copiez-colliez ici.

EDIT : Je suppose que je n'ai pas vraiment comprendre ce que Jon disait après tout :). - C'était assez proche, je pense.

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