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.