2

I have an image converted to a base64 string that I need to convert back to an image and attach to a MailMessage.

Here is the relevant code converting it from base64 string to image (I think I can skip the Image object and do this using one memory stream, but had some issues implementing that). Attempting to save the Image to a MemoryStream throws a generic GDI+ error:

Image image = ImageHelper.Base64ToImage(attachment.FieldData);

if (image != null)
{
    using (var ms = new MemoryStream())
    {
        image.Save(ms, ImageFormat.Png); // Throws a generic GDI+ error on Save

        ms.Position = 0;

        var imageAttachment = new Attachment(ms, "image.png", "image/png");

        message.Attachments.Add(imageAttachment);
    }
}

public static class ImageHelper
{
    public static Image Base64ToImage(string base64String)
    {
        if (string.IsNullOrEmpty(base64String))
        {
            return null;
        }

        byte[] imageBytes = Convert.FromBase64String(base64String);

        using (var ms = new MemoryStream(imageBytes, 0, imageBytes.Length))
        {
            ms.Write(imageBytes, 0, imageBytes.Length);

            Image image = Image.FromStream(ms, true);

            return image;
        }
    }
}

I'm able to serve up the raw base64 string elsewhere using an img tag and it works fine so I'm confident that the problem isn't with the base64 string itself:

<img src="data:image/png;base64,<myBase64StringHere>" alt="My Image" width="500" />

I must be doing something wrong in converting it back, but I haven't been able to figure out the issue. Thanks for any help with this!

Elliot Starks
  • 153
  • 1
  • 5
  • 15
  • Are you sure the image has, "[`color management information embedded in the data stream`](https://msdn.microsoft.com/en-us/library/1kcb3wy4(v=vs.110).aspx)"? Maybe you only wanted to "validate the image", in which case use [this overload](https://msdn.microsoft.com/en-us/library/21zw9ah6(v=vs.110).aspx) with `false` for the embedded color stuff: `Image.FromStream(ms, false, true);`. – Quantic Oct 24 '16 at 22:41
  • I tried both `Image.FromStream(ms, false, true)` and `Image.FromStream(ms)`, but still get the same `A generic error occurred in GDI+.` error on `Image.Save(ms, ImageFormat.Png)` – Elliot Starks Oct 24 '16 at 23:03
  • 1
    Think I found your problem from reading [here](http://stackoverflow.com/questions/336387/image-save-throws-a-gdi-exception-because-the-memory-stream-is-closed), you are not using `Bitmap`, but the same thing occurs with [`Image.FromStream()`](https://msdn.microsoft.com/en-us/library/93z9ee4x(v=vs.110).aspx): "You must keep the stream open for the lifetime of the Image". You however are `using` the MemoryStream so it is disposed as the image is returned, and once that happens the `Image` is no longer usable. Maybe your static class needs to return `Tuple`. – Quantic Oct 25 '16 at 15:51
  • That was indeed the problem, thank you so much! If you post this as an answer, I can accept it. – Elliot Starks Oct 25 '16 at 16:40

1 Answers1

2

Image.FromStream(Stream) says, "You must keep the stream open for the lifetime of the Image", but your using statement disposes the stream as the Image is returned. A workaround would be to return both the image and the stream together as a tuple and without the using:

public static Tuple<Image, MemoryStream> Base64ToImage(string base64String)
{
    if (string.IsNullOrEmpty(base64String))
    {
        return null;
    }

    byte[] imageBytes = Convert.FromBase64String(base64String);
    var ms = new MemoryStream(imageBytes, 0, imageBytes.Length)        
    ms.Write(imageBytes, 0, imageBytes.Length);
    Image image = Image.FromStream(ms, true);

    return new Tuple<Image, MemoryStream>(image, ms);
}

Also note to take care and view each overload on the MSDN pages. Normally I would say, "view the most encompassing overload to get all the remarks and notes", but in this case that is not true. The MSDN page for the biggest overload, Image.FromStream Method (Stream, Boolean, Boolean) does not mention that you need to keep the stream open, but I am fairly certain that is a mistake on that particular page.

Quantic
  • 1,779
  • 19
  • 30