Embedding EPiServer VPP images in HTML using base64 encoding

This is a follow up to my previous post about allowing web editors to dynamically change the content of their static error pages (this one). The solution I described would not let us insert any graphical elements requiring database access; such as images from EPiServer’s VPP folders. Here is a way to do it; sample code can be found at the bottom of this post.

Let us start by setting up an EPiServer PageType using PageTypeBuilder so that we have something to work with. Parts covered in the other post are, obviously, omitted since they would only add cutter; hence the new PageType.

EmbeddedImagePage.cs

    [PageType("213F3885-B484-404B-8171-58C4DF91DBA0",
        Filename = "~/CodeSample/EmbeddedImage.aspx",
        Name = "Embedded image page")]
    public class EmbeddedImagePage : TypedPageData
    {
        [PageTypeProperty(
            Type = typeof (PropertyImageUrl),
            EditCaption = "Image to embed")]
        public virtual string ImageToEmbed { get; set; }
    }

We use a PropertyImageUrl (12) to get an URL to image property in EPiServer’s edit mode, thus gaining access to the file manager. It will only be necessary to alter the source part of the HTML image tag, so there is really not any more to this than extending System.Web.UI.WebControls.Image.

ImageEmbeddedBase64.cs

    public class ImageEmbeddedBase64 : System.Web.UI.WebControls.Image
    {
        public override string ImageUrl
        {
            get { return base.ImageUrl; }
            set { base.ImageUrl = GetBase64EncodedImageFromVpp(value); }
        }

What we want to change is the way that the ImageUrl property is assigned. This is what the Image control will use to render its src attribute. Override it, and have it call the encoder method (15) described below instead. I will leave out parts that are not all that interesting; they are all included in the downloadable at the top of this post.

        private static string GetBase64EncodedImageFromVpp(string imageUrl)
        {
            var unifiedFile = HostingEnvironment.VirtualPathProvider.GetFile(imageUrl) as UnifiedFile;
            var mimeMapping = MimeMapping.GetMimeMapping(unifiedFile.Name);

The first thing that we need to do is retrieve the image file from the EPiServer VirtualPathProvider (22). This UnifiedFile will be null if it is not in there *, but if the PropertyImageUrl is set, it probably will be. We will need the MIME type of the image when compiling our output from this method, so get it from the unifiedFile (24).

            var memoryStream = new MemoryStream();
            using (var stream = unifiedFile.Open())
            {
                var image = Image.FromStream(stream);
                image.Save(memoryStream, image.RawFormat);
            }

Next, it is time to get the content of the image. Open the unifiedFile into a Stream, and pour it into an Image object (29). Then save the image to a MemoryStream using the proper image format. This is done since we need to convert the file into a byte array to be able to encode it (32).

            var base64String = Convert.ToBase64String(memoryStream.ToArray());

            return string.Format("data:{0};base64,{1}", mimeMapping, base64String);

The format of the src attribute should be as above. First specify what kind of data will be found, then the type of encoding followed by the data string itself. If we would like to encode the EPiServer logotype below (downloaded from here, by the way), it would give us the following HTML output.

EPiServer logotype

<img id="embeddedBase64Image" src="data:image/gif;base64,R0lGOD -- long encoded string goes here -- lAAAA7" alt="EPiServer logotype" />

For obvious reasons I stripped away most of the encoded string, but it is all there if you bother finding the image in the source HTML of this post. Using this extended Image control is no big deal; just register the namespace in your page template and you are good to go.

EmbeddedImage.aspx

<%@ Register TagPrefix="CodeSample" Namespace="EPiServer.CodeSample" Assembly="EPiServer.Templates.AlloyTech" %>

I have been using an EPiServer AlloyTech installation for creating my code samples, as you can see in my assembly reference. The ImageEmbeddedBase64 control will have the same properties as the normal Image control, but use our overidden ImageUrl property instead.

        <CodeSample:ImageEmbeddedBase64
            ID="embeddedBase64Image"
            ImageUrl="<%# CurrentPage.ImageToEmbed %>"
            runat="server" />

Just remember to DataBind the control if you do like above, or the ImageUrl will find itself rather empty.

Getting your image file from other sources

* If you would like to feed this control other image URLs than those pointing to the EPiServer VPPs, it is of course possible to do so. Getting files from a location on the server hard drive would probably include reading from a FileStream instead.

var physicalImagePath = HttpContext.Current.Server.MapPath(imageUrl);
using (var fileStream = File.Open(physicalImagePath, FileMode.Open))
{

Or creating a WebRequest if the images are placed on a remote website; something like below would probably do.

using (var webResponse = WebRequest.Create(imageUrl).GetResponse())
{
    mimeType = webResponse.ContentType;
    using (var stream = webResponse.GetResponseStream())
    {
        return Image.FromStream(stream);
    }
}

Source code

The examples in the post are for the initial version 1.0.0.0. Since the code was moved to a separate Visual Studio project in version 1.1.0.0 you would have to use the new namespace and assembly instead from this version forward.

Source code: EmbeddedBase64Image.zip (Initial version 1.0.0.0)
Source code: Base64EmbeddedImage_src_1.1.0.0.zip (Latest)
Droppable binary: Base64EmbeddedImage_binary_1.1.0.0.zip (Latest)
GitHub-link: https://github.com/matkun/Blog/tree/master/Base64EmbeddedImage