Image.Save (..) видає виняток GDI +, оскільки потік пам'яті закритий


108

У мене є кілька двійкових даних, які я хочу зберегти як зображення. Коли я намагаюся зберегти зображення, він видає виняток, якщо потік пам'яті, який використовується для створення зображення, був закритий перед збереженням. Причиною цього я є те, що я динамічно створюю зображення і як такі .. мені потрібно використовувати потік пам'яті.

це код:

[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.
}

Хтось має пропозиції щодо того, як я міг би зберегти зображення із закритим потоком? Я не можу покластися на розробників, які пам’ятають закрити потік після збереження зображення. Фактично, у розробника не було б НЕ IDEA, щоб зображення генерувалося за допомогою потоку пам'яті (тому що це відбувається в якомусь іншому коді в іншому місці).

Я дуже розгублений :(


1
Я отримав цей коментар від @HansPassant в іншому запитанні . Цей виняток ви отримаєте щоразу, коли у кодека виникнуть проблеми з написанням файлу. Хорошим заявою налагодження для додавання є System.IO.File.WriteAllText (шлях, "тест") перед викликом Save (), він перевіряє основну здатність створювати файл. Тепер ви отримаєте хороший виняток, який говорить вам, що ви зробили не так.
— Хуан Карлос Оропеза

Ви повинні image2.Зберегти всередині usingблоку. Я думаю, що originalBinaryDataStream2 автоматично було утилізовано наприкінці використання. І це кине виняток.
— taynguyen

Відповіді:


172

Оскільки це MemoryStream, вам дійсно не потрібно закривати потік - нічого поганого не відбудеться, якщо цього не зробити, хоча очевидно, що в будь-якому разі викидати все, що є одноразовим, є хорошою практикою. (Докладніше про це див. У цьому питанні .)

Однак вам слід розпоряджатися Bitmap - і це закриє потік для вас. Як правило, коли ви даєте конструктору Bitmap потік, він "володіє" потоком, і ви не повинні закривати його. Як кажуть документи для цього конструктора :

Ви повинні тримати потік відкритим протягом життя Bitmap.

Я не можу знайти жодного документа, який обіцяв би закрити потік, коли ви розпоряджаєтеся растровою картою, але ви повинні мати можливість перевірити це досить легко.


2
приголомшливий! це чудова відповідь, Джон. Здійснює ідеальний сенс (і я пропустив трохи про потік у документах). Два великих пальця вгору! Я відзвітуюся, коли мені це
— вдасться

Будь-які коментарі щодо того, як зробити це, якщо ми хочемо дотримуватися правила CA2000? (msdn.microsoft.com/en-us/library/ms182289.aspx)
— Патрік Шалапський,

@Patrick: Це просто не застосовується - ви в основному перевели право власності на ресурс. Найближчим до вас можна було б створити обгортку "NonClosingStream", яка ігнорує виклик Dispose. Я думаю, що я, можливо, маю його в MiscUtil - не впевнений ...
— Джон Скіт

Дякую за інформацію @Jon. Для мене з якихось дивних причин вона працювала навіть з dispose () у місцевому середовищі розробників, але не працювала у виробництві.
— Oxon

92

Загальна помилка сталася в GDI +. Також може виникнути внаслідок неправильного шляху збереження ! Мені потрібно було півдня, щоб помітити це. Тому переконайтеся, що ви двічі перевірили шлях для збереження зображення.


4
Я радий, що це я бачив, мій шлях був C\Users\mason\Desktop\pic.png. Відсутня кишка! Я б витратив назавжди, перш ніж помітив це.
— муляр

4
Неправильно також означає, що папка, в яку потрібно зберегти зображення, не існує.
— Румер

14

Можливо, варто згадати, що якщо каталог C: \ Temp не існує, він також викине цей виняток, навіть якщо ваш потік все ще існує.


+1 Цей виняток, мабуть, трапляється в різних сценаріях. Недійсний шлях - це той, з ким я сьогодні стикався.
— Кірк Бродхерст

4

У мене була така ж проблема, але насправді причиною було те, що програма не мала дозволу зберігати файли на C. Коли я змінив на "D: \ ..", картинка була збережена.


2

Скопіюйте растрову карту. Ви повинні тримати потік відкритим протягом життя растрової карти.

Під час малювання зображення: System.Runtime.InteropServices.ExternalException: у 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
Це не працює точно; у вашому коді в ToImage (), локальне "зображення" буде правильно мати.
— Патрік Шалапський

Не впевнений, наскільки це RawFormatважливо, хоча. Якщо ви хочете скористатися цим, вийміть його з об'єкта десь на шляху, але в цілому збережіть, як би ви хотіли мати тип .
— Nyerguds

2

Ви можете спробувати створити іншу копію растрової карти:

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

2

Ця помилка трапилася у мене, коли я намагався від Citrix. На папці зображень на сервері було встановлено значення C: \, для чого я не маю привілею. Після того, як папку зображень було переміщено на спільний диск, помилка зникла.


1

Загальна помилка сталася в GDI +. Це може статися через проблеми зі збереженням зображень, я отримав цю помилку, оскільки мій шлях зберігання занадто довгий, я виправив це, спершу зберігаючи зображення в найкоротший шлях і переміщуючи його в правильне місце за допомогою довгих методів обробки шляху.


1

Я отримував цю помилку, оскільки автоматичний тест, який я виконував, намагався зберігати знімки у папку, яка не існувала. Після створення папки помилка усунена


0

Одне дивне рішення, яке змусило мій код працювати. Відкрийте зображення фарбою та збережіть його як новий файл із тим самим форматом (.jpg). Тепер спробуйте з цим новим файлом, і він працює. Це чітко пояснює вам, що файл може бути яким-небудь чином пошкоджений. Це може допомогти, лише якщо у вашому коді виправлені всі інші помилки


0

Це також з'явилося зі мною, коли я намагався зберегти зображення на шляху

C:\Program Files (x86)\some_directory

і .exeне виконаний для запуску адміністратора, я сподіваюся, що це може допомогти тому, у кого теж є проблема.


0

Для мене код нижче A generic error occurred in GDI+вийшов з ладу в рядку, який зберігається в MemoryStream. Код працював на веб-сервері, і я вирішив його, зупинившись і запустивши пул програм, на якому працював сайт.

Повинно була якась внутрішня помилка в 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

Я зіткнувся з цією помилкою, коли спробував просте редагування зображень у програмі WPF.

Встановлення джерела елемента зображення в растрове зображення запобігає збереженню файлів. Навіть встановлення Source = null не видає файл.

Тепер я просто ніколи не використовую зображення як елемент Джерело зображення, тому я можу перезаписатись після редагування!

EDIT

Почувши про властивість CacheOption (завдяки @Nyerguds), я знайшов рішення: тому замість використання конструктора Bitmap я повинен встановити Uri після налаштування CacheOption BitmapCacheOption.OnLoad. ( Image1Нижче - Wpf Imageелемент)

Замість

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

Використання:

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

Дивіться це: Кешування зображень WPF


1
Зображення WPF мають певний параметр BitmapCacheOption.OnLoadдля відключення їх від джерела завантаження.
— Nyerguds

Дякую @Nyerguds, до Вашого коментаря я не зміг поставити правильних запитань
— mkb

0

Спробуйте цей код:

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

Я використовував imageprocessor для зміни розміру зображень, і одного дня я отримав "Загальну помилку в GDI +" виключення.

Подивившись деякий час, я спробував переробити пул додатків і бінго він працює. Тож я зазначу це тут, сподіваюся, це допоможе;)

Ура

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.