Inline Images with ASP.NET Core
The most common way to show an image in an HTML page is to use the <img> tag to load an external resource. Another option is to use a URL that is a Base64 encoded version of the image. There are some aspects worth considering:
- Using this approach the HTML will become larger, because it will also contain the image as a Base64 encoded string
- There is no need to load external resources together with the HTML page, which may make the loading faster
- The images can be cached together with the page
- Of course, if the Base64 string is hardcoded in the page, to change the image, we must change the page
I am a big fan of ASP.NET Core’s tag helpers, and I already wrote about them. This time, I propose a tag helper for rendering a Base64 image from a local file!
Here is the code
[HtmlTargetElement("inline-img")]
public class InlineImage : TagHelper
{
private readonly IWebHostEnvironment _environment;
private static readonly FileExtensionContentTypeProvider _contentTypeProvider = new FileExtensionContentTypeProvider();
public InlineImage(IWebHostEnvironment environment) {
this._environment = environment;
}
[HtmlAttributeName("id")]
public string Id { get; set; }
[HtmlAttributeName("src")]
public string Src { get; set; }
[HtmlAttributeName("style")]
public string Style { get; set; }
[HtmlAttributeName("class")]
public string Class { get; set; }
[HtmlAttributeName("width")]
public string Width { get; set; }
[HtmlAttributeName("height")]
public string Height { get; set; }
[HtmlAttributeName("title")]
public string Title { get; set; }
[HtmlAttributeName("alt")]
public string Alt { get; set; }
public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (!string.IsNullOrWhiteSpace(this.Src))
{
string absoluteSrc;
if (this.Src.StartsWith("~/"))
{
absoluteSrc = this.Src.Replace("~/", this._environment.WebRootPath);
}
else
{
var src = this.Src.StartsWith("/") ? this.Src.Substring(1) : this.Src;
absoluteSrc = Path.Combine(this._environment.WebRootPath, src);
}
var img = File.ReadAllBytes(absoluteSrc);
var ext = Path.GetExtension(absoluteSrc);
_contentTypeProvider.TryGetContentType(ext, out var contentType);
var urlEncoded = $"data:{contentType};base64, {Convert.ToBase64String(img)}";
output.TagName = "img";
output.TagMode = TagMode.SelfClosing;
output.Attributes.Add("src", urlEncoded);
if (!string.IsNullOrWhiteSpace(this.Id))
{
output.Attributes.Add("id", this.Id);
}
if (!string.IsNullOrWhiteSpace(this.Style))
{
output.Attributes.Add("style", this.Style);
}
if (!string.IsNullOrWhiteSpace(this.Class))
{
output.Attributes.Add("class", this.Class);
}
if (!string.IsNullOrWhiteSpace(this.Width))
{
output.Attributes.Add("width", this.Width);
}
if (!string.IsNullOrWhiteSpace(this.Height))
{
output.Attributes.Add("height", this.Height);
}
if (!string.IsNullOrWhiteSpace(this.Title))
{
output.Attributes.Add("title", this.Title);
}
if (!string.IsNullOrWhiteSpace(this.Alt))
{
output.Attributes.Add("alt", this.Alt);
}
}
return base.ProcessAsync(context, output);
}
}
First, this code has absolutely no error checking, you can add it as an exercise!
This class inherits from the TagHelper base class. It is declared as being triggered by an <inline-img> tag, and it requires a property Source to be passed and then checks to see if its relative to the virtual path (starts with ~/), in which case, it replaces it by the actual path. Otherwise, it considers it relative to the root folder.
As suggested in a comment, I am using the FileExtensionContentTypeProvider to retrieve the content type from the file extension.
There are a few properties that sometimes come in handy when dealing with images: Id, Style, Class, Width, Height, Alt and Title, all optional. Feel free to add others.
To use this, you need to register this tag helper, possibly on the _ViewImports.cshtml file:
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, MyWebApplication
Just replace MyWebApplication by the full name of your assembly. After this, it’s just a matter of declaring an <inline-img> element on a Razor view:
<inline-img src="my.logo.png" />
And, presto, you will end up with something like this:
<img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEU…………." />
Hope you enjoy this trick!