Search code examples
c#image-processingbase64system.net.mail

Embedding .jpg image (base64 string representation) into email message body (System.Net.Mail)


I am able to convert a jpg image to base64String properly. But I am having trouble using that converted image string and LinkedResource to to embed it in an email body. The image comes up as an image not found icon in the email body. Any help would be greatly appreciated.

I've followed the example at this link: Iterate through an html string to find all img tags and replace the src attribute values

I am using HtmlAgilityPack (nuget package) to target the img element with the code below.

private string embedImageInMail(string html)
       {
            HtmlAgilityPack.HtmlDocument doc = new 
HtmlAgilityPack.HtmlDocument();
            doc.LoadHtml(html);
            doc.DocumentNode.Descendants("img").Where(e =>
            {
                string src = e.GetAttributeValue("src", null) ?? "";
                return !string.IsNullOrEmpty(src) && src.StartsWith("data:image");
            })
            .ToList()
            .ForEach(x =>
            {
                string currentSrcValue = x.GetAttributeValue("src", null);
                currentSrcValue = currentSrcValue.Split(',')[1];    //Base64 part of string
                byte[] imageData = Convert.FromBase64String(currentSrcValue);
                string contentId = Guid.NewGuid().ToString();
                using (MemoryStream ms = new MemoryStream(imageData))
                {
                    LinkedResource inline = new LinkedResource(ms, "image/jpeg");
                    inline.ContentId = contentId;
                    inline.TransferEncoding = System.Net.Mime.TransferEncoding.Base64;
                    x.SetAttributeValue("src", "cid:" + inline.ContentId);
                }
            });

        return doc.DocumentNode.OuterHtml; 
    }

The html parameter passed the function contains an img tag with src equal to the base64 encoding of the image. What gets returned from this function, gets assigned to message.body of the email.


Solution

  • Solved by following solutions in other posts. If anyone else out there is trying to do the same thing and am having trouble like I was, I'll post code below.

    Basically I had to save the LinkedResource (images) in the html to a List and then iterate over that list and add all the images to the AlternateView outside of the foreach loop.

     // Embeds image to properly show in Email. Image element to show in html will not work in Email body. 
                            // https://stackoverflow.com/questions/39785600/iterate-through-an-html-string-to-find-all-img-tags-and-replace-the-src-attribut
                            // XmlAgilityPack gets used here to parse html string. 
                            List<LinkedResource> linkedResources = new List<LinkedResource>(); 
                            HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
                            int increment = 0; 
                            doc.LoadHtml(tempMsg);
                            doc.DocumentNode.Descendants("img").Where(z =>
                                {
                                    string src = z.GetAttributeValue("src", null) ?? "";
                                    return !string.IsNullOrEmpty(src) && src.StartsWith("data:image");
                                })
                                .ToList()
                                .ForEach(x =>
                                {
                                    string currentSrcValue = x.GetAttributeValue("src", null);
                                    currentSrcValue = currentSrcValue.Split(',')[1];    //Base64 part of string
                                    byte[] imageData = Convert.FromBase64String(currentSrcValue);
                                    string id = Guid.NewGuid().ToString();
                                    increment++;
    
                                    LinkedResource inlineImage = new LinkedResource(new MemoryStream(imageData), System.Net.Mime.MediaTypeNames.Image.Jpeg);
                                    inlineImage.ContentType.Name = "img " + increment; 
                                    inlineImage.ContentId = id;
                                    inlineImage.TransferEncoding = System.Net.Mime.TransferEncoding.Base64;
                                    x.SetAttributeValue("src", "cid:" + inlineImage.ContentId);
                                    linkedResources.Add(inlineImage);
                                });
    
                            // Append multiple images from template to email message.
                            // https://stackoverflow.com/questions/7048758/how-to-embed-multiple-images-in-email-body-using-net
                            // Write html to message.body 
                            AlternateView altView = AlternateView.CreateAlternateViewFromString(doc.DocumentNode.OuterHtml, null, "text/html");
                            linkedResources.ForEach(x=>altView.LinkedResources.Add(x));
                            message.AlternateViews.Add(altView);
    
                            String[] attachments = txtAttachFiles.Text.Split(';');
                            foreach (String filename in attachments)
                            {
                                if (File.Exists(filename))
                                {
                                    Attachment attachment = new Attachment(filename);
                                    message.Attachments.Add(attachment);
                                }
                            }
    
                            // loop is set to 1 in the app.config file so technically this for loop is not needed, but will keep this loop just in case. 
                            for (int loop = 0; loop < Convert.ToInt32(ConfigurationManager.AppSettings["loop"]); loop++)
                            {
                                SmtpClient smtp = new SmtpClient();
                                smtp.Host = ConfigurationManager.AppSettings["mailHost"];
                                smtp.Port = Int32.Parse(ConfigurationManager.AppSettings["mailPort"]);
                                smtp.UseDefaultCredentials = Convert.ToBoolean(ConfigurationManager.AppSettings["mailDefaultCredentials"]);
                                smtp.Send(message);
                            }
                        }