Произошла общая ошибка в GDI +, JPEG Image для MemoryStream - PullRequest
302 голосов
/ 27 июня 2009

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

Странно, но это прекрасно работает с 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.

Ответы [ 30 ]

171 голосов
/ 27 июня 2009

ОК. Кажется, я нашел причину просто благодаря удаче, и в этом конкретном методе нет ничего плохого, он еще больше скопировал стек вызовов.

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

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

Похоже, что поток памяти, в котором объект был создан на , должен быть открытым во время сохранения объекта. Я не уверен, почему это так. Кто-нибудь может просветить меня и как я могу обойти это.

Я только возвращаюсь из потока, потому что после использования кода изменения размера, подобного this , у файла назначения есть неизвестный тип MIME (img.RawFormat.Guid), и Id, как тип MIME, чтобы быть корректным на всех изображения объектов, так как это затрудняет написание общего кода обработки в противном случае.

EDIT

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

120 голосов
/ 15 сентября 2011

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

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

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

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

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

50 голосов
/ 10 мая 2011

Я добавлю эту причину ошибки также в надежде, что это поможет какому-нибудь будущему интернет-путешественнику. :)

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

Жаль, что в конструкторе Bitmap.

нет дружественного .net ArgumentException
37 голосов
/ 20 декабря 2010

В этой статье подробно объясняется, что именно происходит: Зависимости конструктора растровых изображений и изображений

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

Итак, вместо

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

попробуйте это

Stream imageStream;
...

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

и закрытие imageStream при закрытии формы или закрытии веб-страницы.

28 голосов
/ 09 ноября 2011

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

Если вы не уверены на 100%, что путь к файлу доступен, а права доступа правильные, попробуйте записать текстовый файл. Это займет всего несколько секунд, чтобы исключить то, что было бы очень простым исправлением.

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

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

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

16 голосов
/ 16 апреля 2012

Сохранить изображение в растровую переменную

using (var ms = new MemoryStream())
{
    Bitmap bmp = new Bitmap(imageToConvert);
    bmp.Save(ms, format);
    return ms.ToArray();
}
13 голосов
/ 24 июня 2013

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

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

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

13 голосов
/ 19 сентября 2016

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

Стоит сказать, что ваш путь отстой, чем "Произошла общая ошибка в GDI +"

13 голосов
/ 07 мая 2014

Я также получил эту ошибку при сохранении 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;
}
11 голосов
/ 30 ноября 2012

Это расширение / уточнение ответа Фреда, который гласил: «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-аналоги.

...