Загальна помилка сталася в GDI +, JPEG Image в MemoryStream


326

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

Дивно, що це відмінно працює з png, але дає вищевказану помилку у jpg та gif, що досить заплутано.

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

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

Більш докладно до винятку. Причина, через яку виникає так багато проблем, - відсутність пояснень :(

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: 

Добре, що я спробував поки що.

  1. Клонування зображення та робота над цим.
  2. Отримання кодера для того MIME, що передає його з налаштуваннями якості jpeg.



3
Для мене проблема полягала в тому, що папки не існувало. Виправлено просто створенням папки.
газик

Для мене це був показник поза діапазоном проковтування.
Біллі Джейк О'Коннор

Відповіді:


189

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

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

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

Видається, що потік пам'яті, на якому створений об'єкт , повинен бути відкритим під час збереження об'єкта. Я не впевнений, чому це так. Хтось здатний мене просвітити і як я можу це обійти.

Повертаюсь лише з потоку, оскільки після використання коду зміни розміру подібний до цьогоПовертаюсь файл призначення має невідомий тип mime (img.RawFormat.Guid), і я подібний до типу Mime, щоб бути правильним для всіх об'єктів зображення, оскільки він важко пише загальний обробка коду в іншому випадку.

EDIT

Це не з’явилося в моєму початковому пошуку, але ось відповідь від Джона Скіта


4
Я не усвідомлював, що коли ви отримуєте растровий файл із потоку пам'яті, ви не повинні закривати потік. дуже корисно, дякую
mcdon

38
Дякую. Це, ймовірно, врятувало останнє моє волосся.
NotMe

6
Дякую! це заощадило мені багато часу, одна річ, чи не заперечуєте ви, щоб підкреслити причину помилки на початку вашої відповіді, оскільки я (і, мабуть, більшість помилок) пропустив її за оригінальним сном через відповіді, можливо, щось на кшталт " НЕ
ЗАКРІЙТЕ ПАМ'ЯТУ

6
Яка ваша змінна "dst"?
WEFX

1
@madcapnmckay, будь ласка, поясніть, що таке змінна 'dst' та її значення
Mike T

131

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

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

Якщо ви використовуєте XP, не забудьте додати дозвіл на запис для акаунта aspnet у цій папці.

Якщо ви використовуєте Windows Server (2003,2008) або Vista, переконайтеся, що додайте дозвіл на запис для облікового запису послуги мережі.

Сподіваюся, це допоможе комусь.


7
Ви цього не зробили! Я витратив дві години на прокляті дозволи на написання ... Прийшов сюди, щоб написати це. Сподіваюся, ви отримаєте більше подій. :)
Gleno

2
ЦЕ було для мене рішенням. +1 цілком!
Grandizer

5
Ви можете зробити File.WriteAllText ("filename.jpg", ""), а потім File.DeleteFile ("filename.jpg") перед збереженням растрової карти. У моєму орієнтирі це займає лише 0,001 секунди, і ви отримуєте приємне "Ви не маєте дозволу зберігати filename.jpg там"
Despertar

@Despertar Ви маєте на увазі File.Delete (), але це дуже зручна хитрість! Безумовно, я буду користуватися цим, коли я зберігаю растрові карти.
D Coetzee

2
У моєму випадку каталог не існував.
мовчазне повідомлення

54

Я додам цю причину помилки також, сподіваючись, що вона допоможе майбутньому мандрівнику в Інтернеті. :)

GDI + обмежує максимальну висоту зображення до 65500

Ми робимо основні зміни розміру зображення, але при зміні розміру ми намагаємося підтримувати співвідношення сторін. У нас є QA хлопець, який трохи занадто хороший у цій роботі; він вирішив перевірити це на фотографії, що має ширину ОДИН піксель, висотою 480 пікселів. Коли зображення було масштабовано відповідно до наших розмірів, висота становила на північ від 68 000 пікселів, а наш додаток вибухнув A generic error occurred in GDI+.

Ви можете перевірити це самостійно за допомогою тесту:

  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+."
  }

Це дуже погано, що не існує дружнього .net, ArgumentExceptionкинутого в конструктор Bitmap.


17
Дякую - цей мандрівник в Інтернеті за часів дуже вдячний за те, що ви залишили це повідомлення.
Том Вест

З мого тестування, 65535 - це фактично максимальне значення. У 65536 я починаю бачити загальну помилку.
ChaseMedallion

Просто спробував це знову: Win10 .net 4.5 і .net 4.6.1, і він підірвався на рівні 65501, що здається ще більш випадковим. Код також загрожує синтаксичними помилками, оновиться :)
Фред

37

У цій статті докладно пояснюється, що саме відбувається: залежність бітової карти та конструктора зображень

Коротше кажучи, протягом життя Imageпобудованого з потоку потоку не слід руйнувати.

Отже, замість

using (var strm = new ... )  {
    myImage = Image.FromStream(strm);
}

спробуйте це

Stream imageStream;
...

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

і закрити imageStream у формі закрити або закрити веб-сторінку.


Так, ця ця отримала мене. Я був сумлінним і завернув свій потік у, usingа пізніше спробував скопіювати зображення в потік пам'яті і отримав жахливе повідомлення "Загальна помилка в GDI +".
Чи буде Appleby

Ваше посилання дало мені нескінченні переадресації; ця працює. У мене виникли проблеми із економією, PixelFormat.Format32bppArgbале ні PixelFormat.Format1bppIndexed. У статті, яку ви пов’язали, пояснюється, чому: GDI + може вибрати повторне декодування даних растрових зображень з вихідного потоку, а не зберігати все в пам'яті. Думаю, що він не перекодує зображення 1bpp.
labreuer

Навіть нове посилання більше не працює. Простий пошук у Google, схоже, не виявив правильну сторінку. Але я був дуже радий знайти цю відповідь! Моя робота над копіюванням у нову растрову карту не вдалася через цю проблему
Katjoek

28

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

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

var img = System.Drawing.Image.FromStream(incomingStream);

// img.Save(path);
System.IO.File.WriteAllText(path, "Testing valid path & permissions.");

І не забудьте почистити файл.


Це питання для мене ... Я хотів би, щоб помилка була менш розпливчастою, врятувала б мені багато часу.
Oofpez

Так! Папка, яку ви зберігаєте, повинна існувати. Я зараз роблю перевірку на це спочатку, перш ніж спробувати зберегти зображення. (Все-таки помилка мене вигадує, приблизно раз на рік.)
Магнус Сміт

Мій шлях був каталогом, а не файлом.
Асен

20

Збережіть зображення у растровій змінній

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

Це вирішило мою проблему. Чи можете ви пояснити, чому збереження зображення в Bitmap відлякує виняток?
jmc

Врятував мій день .. не знаю, що спричинило проблему, але Bitmap зберігає роботу .. System.Drawing.Image не збережеться в потоці пам'яті, але Bitmap робить !!!
Сан

Це було найкращим рішенням для мене. Створення нового Bitmap та перетворення з нього.
uzay95

17

Про всяк випадок, якщо хтось робить такі дурні речі, як я. 1. переконайтесь, що шлях існує. 2. переконайтеся, що у вас є дозволи на запис. 3. переконайтеся, що ваш шлях правильний, у моєму випадку у програмі TargetPath відсутнє ім’я файлу :(

це повинно було сказати, ваш шлях смокче, ніж "Загальна помилка в GDI +"


16

Я також отримав цю помилку під час збереження JPEG, але лише для певних зображень.

Мій кінцевий код:

  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
  }

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

Це моя функція SaveJpeg просто FYI:

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

1
Це вирішили дні витягування волосся. Це найбільш wtf-код, на який я думаю, що я коли-небудь писав :)
Джефф Данлоп

13

Я виявив, що якби одна з батьківських папок, де я зберігав файл, мала простір, GDI + видасть загальне виключення.

Іншими словами, якщо я спробував зберегти "C: \ Документи та параметри \ myusername \ Локальні налаштування \ Temp \ ABC DEF M1 Trended Values ​​\ Images \ picture.png", то він викинув загальний виняток.

Ім'я моєї папки генерується з імені файлу, у якого, можливо, залишився пробіл, тому було легко.


3
приголомшливо - я б ніколи не думав дивитись на шлях до каталогу, який уважно
jharr100,

11

якщо ваш код наступний, то також виникає ця помилка

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

Правильний є

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

Це може бути тому, що ми повертаємося з використовуючого блоку


для мене це було повернення в блок використання. Я все ще використовую використання, але повертаю значення поза блоком. Дякую!
Драгуф

1
Я з'ясував "важкий шлях", що якщо ви знову збережете це зображення в новий потік (наприклад, HttpContext.Response.OutputStream), вам також потрібно буде виконати stream.Flush (), якщо не помилка знову.
Лучан

11

Це розширення / кваліфікація відповіді Фреда, яка заявила: "GDI обмежує висоту зображення до 65534". Ми зіткнулися з цим питанням за допомогою одного з наших .NET-додатків, і, побачивши публікацію, наша команда з аутсорсингу підняла руки в повітря і сказала, що не зможе вирішити проблему без великих змін.

На основі мого тестування можна створювати / маніпулювати зображеннями висотою більше 65534, але проблема виникає під час збереження у потоці чи файлі В ДРУГИХ ФОРМАХ . У наступному коді виклик методу t.Save () кидає нашому другові загальний виняток, коли висота пікселя для мене становить 65501. З причини цікавості я повторив тест на ширину і той самий ліміт, що застосовується до економії.

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

Така ж помилка виникає і при записі в потік пам'яті.

Щоб обійти його, ви можете повторити наведений вище код і замінити ImageFormat.Tiff або ImageFormat.Bmp на ImageFormat.Jpeg.

Це досягає висот / ширини для мене 100 000 - я не перевіряв межі. Як це буває. Тиф був життєздатним варіантом для нас.

БУДЬ ЗАПЕРЕДЖЕНО

Потоки / файли TIFF в пам'яті споживають більше пам'яті, ніж їх аналоги JPG.


10

Була дуже схожа проблема, а також спробували клонувати зображення, яке не працює. Я виявив, що найкращим рішенням було створення нового об’єкта Bitmap із зображення, завантаженого з потоку пам'яті. Таким чином потік може бути утилізований, наприклад

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

Сподіваюсь, це допомагає.


6

Помилка сталася через дозвіл. переконайтеся, що в папці ВСЕ ДОЗВІЛ.

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

РЕШЕНО - У мене була така точна проблема. Для мене виправлення полягало в тому, щоб збільшити дискову квоту для IUSR на сервері IIS. У цьому випадку у нас є каталог додатків із зображеннями предметів тощо. Квота на завантаження для "Анонімного веб-користувача" була встановлена ​​в 100 Мб, що є типовим для цього конкретного серверу хостингу IIS компанії. Я збільшив його до 400 Мб і зміг завантажувати зображення без помилок.

Це може бути не вашою проблемою, але якщо це так, це легко виправити.


4

У моєму випадку проблема полягала в шляху, який я економив (корінь C:\). Змінивши його, щоб D:\111\виняток пішов.


4

Ще одна причина цієї помилки - шлях, який ви вказали в методі "Збереження" для екземпляра Bitmap, не існує, або ви не надали повний / дійсний шлях.

Просто була ця помилка, тому що я пропускав ім'я файлу, а не повний шлях!

Буває!


4

Моя черга!

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

Отримав його на .Збережіть ... тому, що за допомогою () тримає файл відкритим, тому я не можу його перезаписати. Можливо, це допоможе комусь у майбутньому.


4

Та сама проблема, з якою я стикався. Але в моєму випадку я намагався зберегти файл на C-диску, і це було недоступно. Тож я спробував це зберегти на D-накопичувачі, який був повністю доступним, і мені це вдалося.

Тому спочатку перевірте свої папки, в яких ви намагаєтеся зберегти. Ви повинні мати усі (читати та писати) права для цієї конкретної папки.


причина зазвичай c не дозволяє без дозволу адміністратора.
Анек Азам Хан

2

Я помічаю, що ваша справа "jpeg" насправді:

            default:
                format = ImageFormat.Jpeg;
                break;

Ви впевнені, що формат jpeg, а не щось інше?

Я б спробував:

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

Або перевірити, що imageToConvert.MimeType()насправді повертається.

ОНОВЛЕННЯ

Чи є якась інша ініціалізація, яку потрібно зробити об’єкту MemoryStream?


Дякую. Це, безумовно, викликається з правильним форматом. Я завантажую jpg, налагоджую і підтверджую, що mime розпізнається як image / jpeg, а формат - JPG.
madcapnmckay

3
Ну добре - я завжди намагаюся усунути очевидне першим. Я не можу порахувати, скільки разів я цього не робив, і пізніше повернувся, щоб вкусити мене.
ChrisF

2
  • У мене ця проблема була на тестовому сервері, але не на живому сервері.
  • Я писав зображення в потік, тому це не було дозволом.
  • Я безпосередньо розгортав деякі .dll на тестовому сервері.
  • Розгортання всього рішення виправило проблему, тож, мабуть, це було дивним невідповідністю компіляції

2

Просто щоб кинути інше можливе рішення на купу, я згадаю випадок, в який я зіткнувся з цим повідомленням про помилку. Цей метод Bitmap.Saveвикине цей виняток під час збереження растрової карти, яку я трансформував і показував. Я виявив, що це не кине виняток, якщо у заяві є точка перелому, а також, якщо це Bitmap.Saveбуло зроблено ранішеThread.Sleep(500) тому я гадаю, що існує якась суперечка щодо ресурсів.

Простого копіювання зображення в новий об’єкт Bitmap було достатньо, щоб запобігти появі цього винятку:

new Bitmap(oldbitmap).Save(filename);

2

У нас була аналогічна проблема з генерацією PDFабо зміною розміру зображення за допомогою lib ImageProcessor на виробничому сервері.

Утилізуйте пул додатків, щоб вирішити проблему.


1

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


1
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

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

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

Наприклад, використовуйте для thar функцію "Random" ( Як працює генератор випадкових чисел C #? ) Або, наприклад, генерують Guid ( http://betterexplained.com/articles/the-quick-guide-to-guids/ )


1

Проста, створити новий екземпляр Bitmap вирішує проблему.

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

0

Для мене я використовував Image.Save(Stream, ImageCodecInfo, EncoderParameters)і, мабуть, це викликало сумновістьA generic error occurred in GDI+ помилку.

Я намагався використати EncoderParameterдля збереження jpegs у 100% якості. Це чудово працювало на "моїй машині" (до!), А не на виробництві.

Коли я використав Image.Save(Stream, ImageFormat)замість цього, помилка зникла! Так, як ідіот, я продовжував використовувати останнє, хоча це зберігає їх у якості за замовчуванням, яке я вважаю лише 50%.

Сподіваюся, ця інформація комусь допомагає.


0

Я також зіткнувся з проблемою. Проблема була пов’язана з розміщенням завантажувального потоку. Але я не розпоряджався цим, це було всередині .Net Framework. Все, що мені потрібно було зробити, це використовувати:

image_instance = Image.FromFile(file_name);

замість

image_instance.Load(file_name);

image_in substance має тип System.Windows.Forms.PictureBox! PictureBox's Load () розпоряджається потоком, з якого завантажено зображення, і я цього не знав.


0

Виходячи з відповіді від @savindra, якщо ви RHM у своїй заяві та спробуєте запуститись як адміністратор, це має вирішити вашу проблему.

Моя, здавалося, була проблемою дозволу.


0

Можливі проблеми, які спричиняють таку помилку:

  1. Каталог не існує (метод, який ви телефонуєте, автоматично не створить цей каталог для вас)
  2. Дозволи безпеки для запису у вихідний каталог не дозволяють користувачу, який запускає додаток, писати

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

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