Saving PNG images to a memory stream causes errors

9

I have a WebAPI endpoint (hosted in IIS) that reads images from a database (byte array) and returns them in the PNG format. This code is simple:

  Image img = ImageHelper.ReadFromDatabase(…);
  using (MemoryStream ms = new MemoryStream())
  {
      img.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
      HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
      response.Content = new ByteArrayContent(ms.ToArray());
      response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
      response.ConfigureResponseCachability(parseConceptVersion, TimeSpan.FromHours(1));
      return response;
  }

I have three web servers and on one of the them the above code causes this error: A generic error occurred in GDI+. at at System.Drawing.Image.Save().

More detail on the servers

The servers are running different OS versions:

  • Server 1, working fine: Windows Server 2012 Standard
  • Server 2, working fine: Windows Server 2008 R2 Standard
  • Server 3, not working: Windows Server 2012 R2 Datacenter (Core)

JPEG is a workaround

I have changed the above code to return a JPEG instead of a PNG and that fixes the problem.

  img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);

I have to send PNG images so I need to find the root cause.

Using WPF classes instead of Windows Form classes

I converted the code to use WPF classes (System.Windows.Media) instead of Windows Form classes. The result is interesting: I get an error creating PNGs as well. This time the error is The component registration is invalid. (Exception from HRESULT: 0x88982F8A) at System.Windows.Media.Imaging.BitmapEncoder.SaveFrame()

Is there something missing on the server?

Could it be that my server is missing a low level component that is necessary to save PNGs?

Thanks


@Chris Pratt asked to see the code that creates the images from the byte array. There are several layers between the WebAPI code and the SqlDataReader invocation but here is the code that converts the byte array read from the database.

/// <summary>
/// Creates a bitmap from a byte array
/// </summary>
public static Bitmap CreateBitmapFromBytes(byte[] bytes, int width, int height)
{
    // Make sure bytes were specified.
    if (bytes == null ||
        bytes.Length == 0)
    {
        return null;
    }

    using (MemoryStream imageStream = new MemoryStream(bytes))
    {
        Bitmap bitmap;

        try
        {
            bitmap = (Bitmap)Bitmap.FromStream(imageStream);
        }
        catch(ArgumentException)
        {
            return GetEmptyBitmap(width, height);
        }

        using (bitmap)
        {
            return new Bitmap(bitmap, width, height);
        }
    }
}
c#
wpf
iis
asp.net-web-api
windows-server-2012-r2
asked on Stack Overflow Mar 7, 2016 by Sylvain • edited Mar 7, 2016 by Sylvain

1 Answer

0

1) The .NET framework >= 3.0 should have a built-in encoder. Check there is an encoder for the right image mime type.

 public static ImageCodecInfo GetEncoderInfo(string mimeType)
        {
             string lookupKey = mimeType.ToLower(); 
            ImageCodecInfo foundCodec = null; 
            if (Encoders.ContainsKey(lookupKey))
            { 
                foundCodec = Encoders[lookupKey];
            } 
            return foundCodec;
        }

(you will need a lookup for encoders)

  private static Dictionary<string, ImageCodecInfo> _encoders;

        /// <summary>
        ///     A quick lookup for getting image encoders
        /// </summary>
        private static Dictionary<string, ImageCodecInfo> Encoders
        {
            get
            {
                if (_encoders == null)
                {
                    _encoders = new Dictionary<string, ImageCodecInfo>();
                }
                if (_encoders.Count == 0)
                {
                    foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageEncoders())
                    {
                        _encoders.Add(codec.MimeType.ToLower(), codec);
                    }
                }
                return _encoders;
            }
        }

Then you can try saving with your favorite encoder or get the bytes

  public static void Save(string path, Image img, string mimeType)
        {
            using (var ms = File.OpenWrite(path))
            {
                ImageCodecInfo encoder = GetEncoderInfo(mimeType);
                img.Save(ms, encoder, null); 
            }
        }

2) Sometimes i had problems saving indexed images. Have a look to yourImage.PixelFormat and try converting image to a different one if needed.

answered on Stack Overflow Mar 10, 2016 by Davide Prenassi

User contributions licensed under CC BY-SA 3.0