2

I have a script that loops through a HTML table for all img tags (using HtmlAgilityPack). It converts any src attributes that have a value that is of a Base64 format, and adds them as a linked resource to a MailMessage object (wrapped within an email).

The below is my test code:

mailMessage.Body = GetFormattedEntries(279); // this loads the HTML table from the DB - using ID 279 as my test

AlternateView avHtml = AlternateView.CreateAlternateViewFromString(mailMessage.Body, null, MediaTypeNames.Text.Html);

HtmlAgilityPack.HtmlDocument htmlDocument = new HtmlAgilityPack.HtmlDocument();
htmlDocument.LoadHtml(mailMessage.Body);
var images = htmlDocument.DocumentNode.Descendants("img").ToList();


foreach (var image in images)
{
    var src = image.Attributes["src"].Value;
    var regex = new Regex(@"data:(?<mime>[\w/\-\.]+);(?<encoding>\w+),(?<data>.*)", RegexOptions.Compiled);
    var match = regex.Match(src);
    var mime = match.Groups["mime"].Value;
    var encoding = match.Groups["encoding"].Value;
    var data = match.Groups["data"].Value;

    byte[] bytes = Convert.FromBase64String(data);
    System.IO.MemoryStream embeddedMs = new System.IO.MemoryStream(bytes, 0, bytes.Length);
    LinkedResource pic1 = new LinkedResource(embeddedMs, new System.Net.Mime.ContentType(mime));
    pic1.TransferEncoding = TransferEncoding.Base64;
    pic1.ContentId = Guid.NewGuid().ToString();
    avHtml.LinkedResources.Add(pic1);
    var newNode = image.CloneNode(true);
    newNode.Attributes["src"].Value = string.Format("cid:{0}", pic1.ContentId);
    image.ParentNode.ReplaceChild(newNode, image);
}


mailMessage.IsBodyHtml = true;
mailMessage.Body = htmlDocument.DocumentNode.OuterHtml;

mailMessage.AlternateViews.Add(avHtml);

When the email is received at the client, the HTML renders, however the embedded images show as a broken image. The Base64 images are not corrupted or malformed as I have tested by saving the Base64 to a local file, which opens perfectly.

The email client I am testing on will show embedded images. This has been enabled.

I have also tested by loading a local file, converting it to a Base64 image and using a similar script to the below to send. That works perfectly.

Community
  • 1
  • 1
Andrew Milici
  • 145
  • 2
  • 10
  • Have you tried [converting](https://codebeautify.org/base64-to-image-converter) the output base64 encoding? If that works, then the email clients might be blocking images due to security, and should probably be added [as an attachment](https://stackoverflow.com/questions/4312687/how-to-embed-images-in-email) – Daniel Park Sep 04 '17 at 05:59
  • Make sure your client supports display of embedded images by default. https://stackoverflow.com/questions/16242489/send-a-base64-image-in-html-email – Navyseal Sep 04 '17 at 06:01
  • Thank you all, I have just updated my question to outline that the client can display embedded images – Andrew Milici Sep 04 '17 at 06:06

1 Answers1

0

I've found the issue for anyone else struggling with this problem. The order of creating the alternative view was the key, in that I was creating the AlternativeView before updating the body with the new CIDs. Here is my updated source which now works:

mailMessage.Body = GetFormattedEntries(279);
var picList = new List<LinkedResource>();


HtmlAgilityPack.HtmlDocument htmlDocument = new HtmlAgilityPack.HtmlDocument();
htmlDocument.LoadHtml(mailMessage.Body);
var images = htmlDocument.DocumentNode.Descendants("img").ToList();

foreach (var image in images)
{

    var src = image.Attributes["src"].Value;
    var regex = new Regex(@"data:(?<mime>[\w/\-\.]+);(?<encoding>\w+),(?<data>.*)", RegexOptions.Compiled);
    var match = regex.Match(src);
    var mime = match.Groups["mime"].Value;
    var encoding = match.Groups["encoding"].Value;
    var data = match.Groups["data"].Value;

    byte[] bytes = Convert.FromBase64String(data);
    System.IO.MemoryStream embeddedMs = new System.IO.MemoryStream(bytes, 0, bytes.Length);
    LinkedResource pic1 = new LinkedResource(embeddedMs, new System.Net.Mime.ContentType(mime));
    pic1.TransferEncoding = TransferEncoding.Base64;
    pic1.ContentId = Guid.NewGuid().ToString();
    picList.Add(pic1);
    var newNode = image.CloneNode(true);
    newNode.Attributes.Remove(newNode.Attributes["class"]);
    newNode.Attributes["src"].Value = string.Format("cid:{0}", pic1.ContentId);
    image.ParentNode.ReplaceChild(newNode, image);
}


mailMessage.IsBodyHtml = true;
mailMessage.Body = htmlDocument.DocumentNode.OuterHtml;

AlternateView avHtml = AlternateView.CreateAlternateViewFromString(mailMessage.Body, null, MediaTypeNames.Text.Html);

foreach (var pic in picList)
    avHtml.LinkedResources.Add(pic);


mailMessage.AlternateViews.Add(avHtml);

mailMessage.SendMail();
Andrew Milici
  • 145
  • 2
  • 10