Произошла общая ошибка в GDI +, JPEG-изображение в 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.
madcapnmckay
источник
связанные: stackoverflow.com/questions/4671449/…
Патрик Салапски
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) и Id как тип MIME, чтобы быть корректным на всех объектах изображения, поскольку это делает его трудным написать универсальный обработка кода в противном случае.

РЕДАКТИРОВАТЬ

Это не появилось в моем первоначальном поиске, но вот ответ от Джона Скита

madcapnmckay
источник
4
Я не осознавал, что когда вы получаете растровое изображение из потока памяти, вы не должны закрывать поток. очень полезно, спасибо
Макдон
38
Спасибо. Это, вероятно, спасло мои последние волосы.
NotMe
6
Спасибо! это сэкономило мне много времени, хотя, не могли бы вы выделить причину ошибки в начале вашего ответа, так как я (и я полагаю, большинство фальшивок) пропустил ее в первоначальном просмотре ответов, возможно что-то вроде " НЕ ЗАКРЫВАЙТЕ ПОТОК ПАМЯТИ, ЕСЛИ ВЫ НАМЕРЕНЫ ИСПОЛЬЗОВАТЬ ИЗОБРАЖЕНИЕ ВНОВЬ ", было бы здорово; D
DorD
6
Какая у вас переменная "dst"?
WEFX
1
@madcapnmckay, пожалуйста, объясните, что такое переменная 'dst' и ее значение
Майк Т
131

Если вы получаете эту ошибку, то я могу сказать, что ваше приложение не имеет разрешения на запись в какой-либо каталог.

Например, если вы пытаетесь сохранить образ из потока памяти в файловую систему, вы можете получить эту ошибку.

Пожалуйста, если вы используете XP, обязательно добавьте разрешение на запись для учетной записи aspnet в этой папке.

Если вы используете Windows Server (2003, 2008) или Vista, убедитесь, что добавили разрешение на запись для учетной записи сетевой службы.

Надеюсь, это поможет кому-нибудь.

Savindra
источник
7
Вы не сделали! Я потратил 2 часа с чертовыми разрешениями на запись ... Пришел сюда, чтобы опубликовать это. Надеюсь, вы получите больше голосов. :)
Глено
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

Мы делаем некоторые базовые изменения размера изображения, но при изменении размера мы стараемся поддерживать соотношение сторон. У нас есть парень по контролю качества, который слишком хорош в этой работе; он решил проверить это с фотографией шириной ОДИН пиксель, которая была 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 +».
Уилл Эпплби
Ваша ссылка давала мне бесконечные перенаправления; этот работает. У меня была проблема с сохранением, PixelFormat.Format32bppArgbно нет PixelFormat.Format1bppIndexed. В статье, на которую вы ссылаетесь, объясняется, почему: GDI + может предпочесть повторно декодировать растровые данные из исходного потока, а не хранить все в памяти. Я предполагаю, что он не перекодирует изображения 1bpp.
labreuer
Даже новая ссылка больше не работает. Простой поиск в Google, похоже, не показал правильную страницу. Но я был очень рад найти этот ответ! Мой
обходной
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
Спас мой день .. не знаю, что вызвало проблему, но сохранение растрового изображения работает .. System.Drawing.Image не будет сохранять в поток памяти, но растровое изображение делает !!!
San
Это было лучшее решение для меня. Создание нового растрового изображения и преобразование из него.
uzay95
17

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

он должен был сказать, что ваш путь отстой, чем "Общая ошибка произошла в GDI +"

ahsant
источник
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 просто для справки:

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;
}
Aximili
источник
1
Это решило дни потянув за волосы. Это самый лучший код, который я когда-либо писал :)
Джефф Данлоп
13

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

Другими словами, если я попытался сохранить в «C: \ Documents and Settings \ myusername \ Local Settings \ Temp \ ABC DEF M1 Trended Values ​​\ Images \ picture.png», он выдал общее исключение.

Имя моей папки было сгенерировано из имени файла, в котором, как оказалось, есть завершающий пробел, поэтому было легко выполнить .Trim () и двигаться дальше.

Igilima
источник
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);        
}

Это может быть потому, что мы возвращаемся из блока использования

dhinesh
источник
для меня это было возвращение в использующий блок. Я все еще использую использование, но я возвращаю значение за пределами блока. Спасибо!
Драгуф
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 был жизнеспособным вариантом для нас.

ИМЕЙТЕ В ВИДУ

Потоки / файлы TIFF в памяти занимают больше памяти, чем их JPG-аналоги.

Vīpes
источник
10

Была очень похожая проблема, а также попытался клонировать изображение, которое не работает. Я обнаружил, что лучшим решением было создание нового объекта Bitmap из изображения, которое было загружено из потока памяти. Таким образом, поток может быть удален, например,

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

Надеюсь это поможет.

HU.
источник
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")
Гауранг с
источник
Я согласен с вами. Я решил эту проблему с РАЗРЕШЕНИЕМ
praguan
5

Решено - у меня была именно эта проблема. Для меня исправлением было увеличение дисковой квоты для IUSR на сервере IIS. В этом случае у нас есть приложение каталога с изображениями предметов и тому подобное. Квота загрузки для «Анонимного веб-пользователя» была установлена ​​равной 100 МБ, что является значением по умолчанию для серверов IIS данной конкретной хостинговой компании. Я увеличил его до 400 МБ и смог загрузить изображения без ошибок.

Возможно, это не ваша проблема, но если это так, то это легко исправить.

Marco
источник
4

В моем случае проблема была в пути, который я сохранял (корень C:\). Его изменение D:\111\сделало исключение исключенным.

Ани
источник
4

Другая причина этой ошибки - путь, который вы указываете в методе Save экземпляра Bitmap, не существует или вы не указали полный / действительный путь.

Просто была эта ошибка, потому что я передавал имя файла, а не полный путь!

Такое случается!

MytyMyky
источник
4

Мой ход!

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

Получил его на .Save ... потому что использование () держит файл открытым, поэтому я не могу перезаписать его. Может быть, это поможет кому-то в будущем.

Энди
источник
4

Та же проблема, с которой я столкнулся. Но в моем случае я пытался сохранить файл на диске C, и он не был доступен. Поэтому я попытался сохранить его на диске D, который был полностью доступен, и мне это удалось.

Поэтому сначала проверьте ваши папки, в которых вы пытаетесь сохранить. У вас должны быть все права (для чтения и записи) для этой конкретной папки.

Jaimin
источник
причина обычно с не позволяет без разрешения администратора.
Анек Азам Хан
2

Я заметил, что ваш случай "JPEG" на самом деле:

            default:
                format = ImageFormat.Jpeg;
                break;

Вы уверены, что формат jpeg, а не что-то еще?

Я бы попробовал:

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

Или проверь что imageToConvert.MimeType() на самом деле возвращается.

ОБНОВИТЬ

Есть ли какая-либо другая инициализация, которую вам нужно сделать для объекта MemoryStream?

ChrisF
источник
Спасибо. Это определенно вызывается в правильном формате. Я загружаю jpg, отлаживаю и подтверждаю, что mime распознается как image / jpeg, а формат - JPG.
madcapnmckay
3
Ну да ладно - я всегда стараюсь сначала устранить очевидное. Я не могу сосчитать, сколько раз я этого не делал, и он вернулся, чтобы укусить меня позже.
ChrisF
2
  • У меня была эта проблема на тестовом сервере, но не на живом сервере.
  • Я записывал изображение в поток, так что это не было проблемой разрешения.
  • Я непосредственно развертывал некоторые из DLL на тестовом сервере.
  • Развертывание всего решения устранило проблему, поэтому, возможно, это было странное несоответствие компиляции
Крис Хэлкроу
источник
2

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

Простого копирования изображения в новый растровый объект было достаточно для предотвращения появления этого исключения:

new Bitmap(oldbitmap).Save(filename);
Segfault
источник
2

У нас была похожая проблема при создании PDF или изменении размера изображения с помощью библиотеки ImageProcessor lib на производственном сервере.

Перезапустите пул приложений, чтобы устранить проблему.

тек-Gayan
источник
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);
}
Jeka
источник
1

Я также получаю эту ошибку, потому что я пытаюсь сохранить изображения с тем же именем предыдущих сохраненных изображений.

Убедитесь, что вы не сохраняете изображения с повторяющимся именем.

Используйте, например, функцию «Случайно» ( как работает генератор случайных чисел в 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чтобы сохранить JPEG в 100% качестве. Это отлично работало на «моей машине» (дох!), А не на производстве.

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

Надеюсь, эта информация кому-то поможет.

Ε Г И І И О
источник
0

Я тоже столкнулся с проблемой. Проблема была в том, что поток загрузки был утилизирован. Но я не распоряжался им, это было внутри .Net framework. Все, что мне нужно было сделать, это использовать:

image_instance = Image.FromFile(file_name);

вместо того

image_instance.Load(file_name);

image_instance имеет тип System.Windows.Forms.PictureBox! Функция PictureBox Load () удаляет поток, из которого было загружено изображение, и я этого не знал.

Клаус
источник
0

Основываясь на ответе @savindra, если вы RHM в своем приложении и пытаетесь работать от имени администратора, это должно решить вашу проблему.

У меня, похоже, была проблема с разрешением.

AltF4_
источник
0

Возможные проблемы, которые вызывают такую ​​ошибку:

  1. Каталог не существует (метод, который вы вызываете, не будет автоматически создавать этот каталог для вас)
  2. Разрешения безопасности для записи в выходной каталог не позволяют пользователю, выполняющему приложение, писать

Надеюсь, это поможет, это исправило мою проблему, я просто убедился, что выходной каталог существует, прежде чем сохранять выходное изображение!

Ихаб Хадж
источник