A generic error occurred in GDI+, JPEG Image to MemoryStream

Viewed 558540

This seems to be a bit of an infamous error all over the web. So much so that I have been unable to find an answer to my problem as my scenario doesn't fit. An exception gets thrown when I save the image to the stream.

Weirdly this works perfectly with a png but gives the above error with jpg and gif which is rather confusing.

Most similar problem out there relate to saving images to files without permissions. Ironically the solution is to use a memory stream as I am doing....

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

More detail to the exception. The reason this causes so many issues is the lack of explanation :(

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: 

OK things I have tried so far.

  1. Cloning the image and working on that.
  2. Retrieving the encoder for that MIME passing that with jpeg quality setting.
35 Answers

OK I seem to have found the cause just by sheer luck and its nothing wrong with that particular method, it's further back up the call stack.

Earlier I resize the image and as part of that method I return the resized object as follows. I have inserted two calls to the above method and a direct save to a file.

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

It appears that the memory stream that the object was created on has to be open at the time the object is saved. I am not sure why this is. Is anyone able to enlighten me and how I can get around this.

I only return from a stream because after using the resize code similar to this the destination file has an unknown mime type (img.RawFormat.Guid) and Id like the Mime type to be correct on all image objects as it makes it hard write generic handling code otherwise.

EDIT

This didn't come up in my initial search but here's the answer from Jon Skeet

This article explains in detail what exactly happens: Bitmap and Image constructor dependencies

In short, for a lifetime of an Image constructed from a stream, the stream must not be destroyed.

So, instead of

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

try this

Stream imageStream;
...

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

and close imageStream at the form close or web page close.

Save image to bitmap variable

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

Had a very similar problem and also tried cloning the image which doesn't work. I found that the best solution was to create a new Bitmap object from the image that was loaded from the memory stream. That way the stream can be disposed of e.g.

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

Hope this helps.

SOLVED - I had this exact problem. The fix, for me, was to up the disk quota for IUSR on the IIS server. In this instance, we have a catalog app with images of items and such. The upload quota for the "Anonymous Web User" was set to 100MB, which is the default for this particular hosting company's IIS servers. I upped it to 400MB and was able to upload images without error.

This might not be your issue, but if it is, it's an easy fix.

Same problem I was facing. But in my case, I was trying to save file in C drive and it was not accessible. So I tried it to save in D drive which was fully accessible and I succeeded.

So first check your folders in which you are trying to save. You must have all (read and write) rights for that particular folder.

Simple, create a new instance of Bitmap solves the problem.

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

I notice that your "jpeg" case is actually:

            default:
                format = ImageFormat.Jpeg;
                break;

Are you sure that the format is jpeg and not something else?

I'd try:

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

Or check what imageToConvert.MimeType() is actually returning.

UPDATE

Is there any other initialisation you need to do to the MemoryStream object?

We had a similar problem on generating a PDF or resize image using ImageProcessor lib on production server.

Recycle the application pool fix the issue.

One other cause of this error and that solve my problème is that your application doesn't have a write permission on some directory.

so to complete the answer of savindra : https://stackoverflow.com/a/7426516/6444829.

Here is how you Grant File Access to IIS_IUSERS

To provide access to an ASP.NET application, you must grant access to the IIs_IUSERS.

To grant read, write, and modify permissions to a specific File or Folder

  1. In Windows Explorer, locate and select the required file.

  2. Right click the file, and then click Properties.

  3. In the Properties dialog box, click the Security tab.

  4. On the Security tab, examine the list of users. (If your application is running as a Network Service, add the network service account in the list and grant it the permission.

  5. In the Properties dialog box, click IIs_IUSERS, and in the Permissions for NETWORK SERVICE section, select the Read, Write, and Modify permissions.

  6. Click Apply, and then click OK.

this worked for me in my IIS of windows server 2016 and local IIS windows 10.

in my case, path was wrong

just use this

String path = Server.MapPath("~/last_img.png");//Path

Possible problems that cause such an error are:

  1. Directory does not exist (The method you are calling will not automatically create this directory for you)
  2. The security permissions to write to the output directory do not allow the user running the app to write

I hope this helps, this was the fix for my issue, I simply made sure that the output directory exists before saving the output image!

My console app got the same error message: "A generic error occurred in GDI+." The error happened in line newImage.Save as refer to the following code.

for (int i = 1; i <= 1000; i++)
{
   Image newImage = Image.FromFile(@"Sample.tif");
   //...some logic here
   newImage.Save(i + ".tif", , ImageFormat.Tiff);
}

The program returned error when the RAM usage is around 4GB, and managed to solve it by changed the Program Target to x64 in project properties.

I have strange solution for this problem. I was fased to this during coding. I thought that Bitmap is struct and wrap around it with method. In my imagination Bitmap will be copies inside method and returned out of method. But later I checked that it's class, and i have no idea why is it helped me, but it Works! Maybe someone have time and have fun to look at IL code of this ;) Not sure maybe it's working cause this method is static inside static method, I don't know.

        public SomeClass
        {
            public byte[] _screenShotByte;
            public Bitmap _screenShotByte;
            public Bitmap ScreenShot 
            { 
                get
                {
                    if (_screenShot == null)
                    {
                        _screenShotByte = ScreenShot();
                        using (var ms = new MemoryStream(_screenShotByte))
                        {
                            _screenShot = (Bitmap)Image.FromStream(ms);
                        }
                         
                        ImageUtils.GetBitmap(_screenShot).Save(Path.Combine(AppDomain.CurrentDomain.BaseDirectory) ,$"{DateTime.Now.ToFileTimeUtc()}.png"));
            
                    }
                    return ImageUtils.GetBitmap(_screenShot);
                }
            }
            public byte[] ScreenShot()
            {
                ///....return byte array for image in my case implementedd like selenium screen shot 
            }
        }
        public static ImageUtils
        {
            public static Bitmap GetBitmap(Bitmap image)
            {
                return Bitmap;
            }
        }


p.s. It's not trolling this solution solved problem of keep using bitmap after saving in another places.

If you have come this far this is something else you can try..

Change your application pool identity setting from ApplicationPoolIdentity to LocalSystem to verify its a permission problem.

However, don't use this setting long-term as it's a security risk; use it only as a diagnosis

Related