web-dev-qa-db-fra.com

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 en tant qu'image. Lorsque j'essaie de sauvegarder l'image, une exception est levée si le flux de mémoire utilisé pour créer l'image a été fermé avant la sauvegarde. La raison pour laquelle je fais cela est parce que je crée dynamiquement des images et en tant que tel .. j'ai besoin d'utiliser un flux de mémoire.

c'est 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 at-il des suggestions sur la façon dont je pourrais enregistrer une image avec le flux fermé? Je ne peux pas compter sur les développeurs pour se rappeler de fermer le flux une fois l'image enregistrée. En fait, le développeur n'aurait AUCUNS IDEA que l'image a été générée à l'aide d'un flux de mémoire (car cela se produit dans un autre code, ailleurs).

Je suis vraiment confus :(

101
Pure.Krome

Comme il s'agit d'un MemoryStream, vous n'avez pas vraiment besoin de fermer le flux. Si vous ne le faites pas, rien de grave ne se produira, bien que, de toute évidence, il soit de bonne pratique d'éliminer tout ce qui est disponible. (Voir cette question } pour plus d'informations.)

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

Vous devez garder le flux ouvert pour le durée de vie du bitmap.

Je ne trouve aucun document promettant de fermer le flux lorsque vous disposez du bitmap, mais vous devriez pouvoir le vérifier assez facilement.

157
Jon Skeet

Une erreur générique s'est produite dans GDI +. Peut aussi provenir de chemin de sauvegarde incorrect ! Il m'a fallu une demi-journée pour le remarquer. Assurez-vous que vous avez bien vérifié le chemin pour enregistrer également l'image.

84
Houman

Il convient peut-être de mentionner que si le répertoire C:\Temp n’existe pas, il lève également cette exception même si votre flux existe toujours.

13
Rojzik

J'ai eu le même problème, mais la cause en était que l'application n'était pas autorisée à enregistrer des fichiers sur le disque C. Lorsque j'ai changé pour "D:\..", l'image a été enregistrée.

3
Morad Aktam

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");
    }
2
Brian Low

Cette erreur m'est arrivée lorsque j'essayais de Citrix. Le dossier de l'image a été défini sur C:\sur le serveur, pour lequel je n'ai pas de privilège. Une fois que le dossier d'image a été déplacé sur un lecteur partagé, l'erreur a disparu.

2
Jay K

Vous pouvez essayer de créer une autre copie de bitmap:

using (var memoryStream = new MemoryStream())
{
    // write to memory stream here

    memoryStream.Position = 0;
    using (var bitmap = new Bitmap(memoryStream))
    {
        var bitmap2 = new Bitmap(bitmap);
        return bitmap2;
    }
}
1
Yuri Perekupko

J'avais cette erreur parce que le test automatisé que j'exécutais essayait de stocker des instantanés dans un dossier inexistant. Après avoir créé le dossier, l'erreur résolue

1
P.Lisa

Une erreur générique s'est produite dans GDI +. Cela peut se produire en raison de problèmes de chemins de stockage d'images. J'ai eu cette erreur parce que mon chemin de stockage est trop long. J'ai résolu ce problème en stockant d'abord l'image dans le chemin le plus court et en la déplaçant à l'emplacement approprié à l'aide de techniques de traitement de chemin long.

1
S.Roshanth

Essayez ce code:

static void Main(string[] args)
{
    byte[] data = null;
    string fullPath = @"c:\testimage.jpg";

    using (MemoryStream ms = new MemoryStream())
    using (Bitmap tmp = (Bitmap)Bitmap.FromFile(fullPath))
    using (Bitmap bm = new Bitmap(tmp))
    {
        bm.SetResolution(96, 96);
        using (EncoderParameters eps = new EncoderParameters(1))
        {   
            eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
            bm.Save(ms, GetEncoderInfo("image/jpeg"), eps);
        }

        data = ms.ToArray();
    }

    File.WriteAllBytes(fullPath, data);
}

private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
        ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();

        for (int j = 0; j < encoders.Length; ++j)
        {
            if (String.Equals(encoders[j].MimeType, mimeType, StringComparison.InvariantCultureIgnoreCase))
                return encoders[j];
        }
    return null;
}
0
BogdanRB

Une solution étrange qui a fait fonctionner mon code. Ouvrez l’image dans Paint et enregistrez-la sous un nouveau fichier de même format (.jpg). Maintenant, essayez avec ce nouveau fichier et cela fonctionne. Cela vous explique clairement que le fichier est peut-être corrompu. Cela ne peut aider que si votre code corrige tous les bogues

0
Vinothkumar

Pour moi, le code ci-dessous s'est écrasé avec A generic error occurred in GDI+sur la ligne qui enregistre dans une MemoryStream. Le code s'exécutait sur un serveur Web et j'ai résolu le problème en arrêtant et en démarrant le pool d'applications exécutant le site.

Il doit y avoir eu une erreur interne dans GDI +

    private static string GetThumbnailImageAsBase64String(string path)
    {
        if (path == null || !File.Exists(path))
        {
            var log = ContainerResolver.Container.GetInstance<ILog>();
            log.Info($"No file was found at path: {path}");
            return null;
        }

        var width = LibraryItemFileSettings.Instance.ThumbnailImageWidth;

        using (var image = Image.FromFile(path))
        {
            using (var thumbnail = image.GetThumbnailImage(width, width * image.Height / image.Width, null, IntPtr.Zero))
            {
                using (var memoryStream = new MemoryStream())
                {
                    thumbnail.Save(memoryStream, ImageFormat.Png); // <= crash here 
                    var bytes = new byte[memoryStream.Length];
                    memoryStream.Position = 0;
                    memoryStream.Read(bytes, 0, bytes.Length);
                    return Convert.ToBase64String(bytes, 0, bytes.Length);
                }
            }
        }
    }
0
mortb

Je suis tombé sur cette erreur lorsque j'essayais une simple retouche d'image dans une application WPF.

Définir la source d'un élément Image sur le bitmap empêche l'enregistrement du fichier . Même définir Source = null ne semble pas libérer le fichier.

Maintenant, je n'utilise plus jamais l'image en tant qu'élément source de l'image, je peux donc écraser après l'édition!

MODIFIER

Après avoir entendu parler de la propriété CacheOption (Merci à @Nyerguds), j'ai trouvé la solution: Donc, au lieu d'utiliser le constructeur Bitmap, je dois définir l'Uri après avoir défini CacheOptionBitmapCacheOption.OnLoad. (Image1 ci-dessous est l'élément Wpf Image)

Au lieu de

Image1.Source = new BitmapImage(new Uri(filepath));

Utilisation:

var image = new BitmapImage();
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(filepath);
image.EndInit();
Image1.Source = image;

Voir ceci: WPF Image Caching

0
mkb