Inline Images in ASP.NET MVC Core
I have blogged extensively about Data URIs in the past. It allows us to render external contents inside of the page’s HTML, avoiding additional HTTP requests, but enlarging the HTML to serve. Sometimes, it does make sense, especially because the whole page can be made cacheable.
MVC does not offer any mechanism for serving images as inline Data URIs, and that is the reason for this post!
For this example, I added an extension method to the IHtmlHelper class, InlineImage:
public static class HtmlHelperExtensions
{
private static string GetFileContentType(string path)
{
if (path.EndsWith(".JPG", StringComparison.OrdinalIgnoreCase) == true)
{
return "image/jpeg";
}
else if (path.EndsWith(".GIF", StringComparison.OrdinalIgnoreCase) == true)
{
return "image/gif";
}
else if (path.EndsWith(".PNG", StringComparison.OrdinalIgnoreCase) == true)
{
return "image/png";
}
throw new ArgumentException("Unknown file type");
}
public static HtmlString InlineImage(this IHtmlHelper html, string path, object attributes = null)
{
var contentType = GetFileContentType(path);
var env = html.ViewContext.HttpContext.ApplicationServices.GetService(typeof (IHostingEnvironment)) as IHostingEnvironment;
using (var stream = env.WebRootFileProvider.GetFileInfo(path).CreateReadStream())
{
var array = new byte[stream.Length];
stream.Read(array, 0, array.Length);
var base64 = Convert.ToBase64String(array);
var props = (attributes != null) ? attributes.GetType().GetProperties().ToDictionary(x => x.Name, x => x.GetValue(attributes)) : null;
var attrs = (props == null)
? string.Empty
: string.Join(" ", props.Select(x => string.Format("{0}=\"{1}\"", x.Key, x.Value)));
var img = $"<img src=\"data:{contentType};base64,{base64}\" {attrs}/>";
return new HtmlString(img);
}
}
}
You can see that I only support three image formats: JPEG, GIF and PNG. These are the safe formats that can be rendered by all browsers. The format is inferred from the file name’s extension.
The InlineImage method takes a local file name that will be considered from the site’s root, and an optional collection of attributes. If present, these attributes are added to the generated IMG tag as-is.
To use this extension, all we need is to add this code in a Razor view:
@Html.InlineImage("image.jpg", new { width = "200", height = "200" });
And the output should look like this (trimmed):
<img src="data:image/jpeg;base64,SGVsbG8sIFdvcmxkIQ%3D%3D...=" width="200" height="200" />
This code works as is in ASP.NET MVC Core, but can be easily changed to work in MVC 5 or prior: just use Server.MapPath to get the physical address of the file to load.