Elliot Starks Elliot Starks - 1 month ago 12
C# Question

Convert Base64 String To Image Attachment

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!

Answer

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.