How to Embed Multiple Images in Email Body Using .Net

How to embed multiple images in email body using .NET

So, I think figured out what the actual problem is
Its in this line

// Alternate view for embedded images
AlternateView avText = AlternateView.CreateAlternateViewFromString(metric.Name, null, MediaTypeNames.Text.Html);
AlternateView avImages = AlternateView.CreateAlternateViewFromString(htmlBody, null, MediaTypeNames.Text.Html);

As you can see, both my views are specified as Text.Html, so the the 1st one is overriding the next one and so I only see text and images are sent as attachments

I made the following change and it worked as expected

AlternateView avText = AlternateView.CreateAlternateViewFromString(metric.Name, null, **MediaTypeNames.Text.Plain**);
AlternateView avImages = AlternateView.CreateAlternateViewFromString(htmlBody, null, MediaTypeNames.Text.Html);

how to send email with multiple embedded images with asp.net 4.0 with c#

class Program
{
static void Main(string[] args)
{
SmtpMail oMail = new SmtpMail("TryIt");
SmtpClient oSmtp = new SmtpClient();

// Set sender email address, please change it to yours
oMail.From = "test@emailarchitect.net";

// Set recipient email address, please change it to yours
oMail.To = "support@emailarchitect.net";

// Set email subject
oMail.Subject = "test html email with attachment";

// Your SMTP server address
SmtpServer oServer = new SmtpServer("smtp.emailarchitect.net");

// User and password for ESMTP authentication, if your server doesn't require
// User authentication, please remove the following codes.
oServer.User = "test@emailarchitect.net";
oServer.Password = "testpassword";

// If your SMTP server requires SSL connection, please add this line
// oServer.ConnectType = SmtpConnectType.ConnectSSLAuto;

try
{
// Import html body and also import linked image as embedded images.
oMail.ImportHtml( "<html><body>test <img src=\"test.gif\"> importhtml</body></html>",
"c:\\my picture", //test.gif is in c:\\my picture
ImportHtmlBodyOptions.ImportLocalPictures | ImportHtmlBodyOptions.ImportCss );

Console.WriteLine("start to send email with embedded image...");
oSmtp.SendMail(oServer, oMail);
Console.WriteLine("email was sent successfully!");
}
catch (Exception ep)
{
Console.WriteLine("failed to send email with the following error:");
Console.WriteLine(ep.Message);
}
}
}

}

Display embed images in HTML emails

If you are saving the messages downloaded from IMAP using MailKit, then you can reload the message like this:

var message = MimeMessage.Load (fileName);

Then, to get the message body text, you can do this:

string body = message.HtmlBody ?? message.TextBody;

Now, to answer your question about how to render images using MimeKit/MailKit, you can take a look at the sample message viewer here: https://github.com/jstedfast/MimeKit/tree/master/samples/MessageReader/MessageReader

The important bit is the HtmlPreviewVisitor:

using System;
using System.IO;
using System.Collections.Generic;

using MimeKit;
using MimeKit.Text;
using MimeKit.Tnef;

namespace MessageReader
{
/// <summary>
/// Visits a MimeMessage and generates HTML suitable to be rendered by a browser control.
/// </summary>
class HtmlPreviewVisitor : MimeVisitor
{
readonly List<MultipartRelated> stack = new List<MultipartRelated> ();
readonly List<MimeEntity> attachments = new List<MimeEntity> ();
string body;

/// <summary>
/// Creates a new HtmlPreviewVisitor.
/// </summary>
public HtmlPreviewVisitor ()
{
}

/// <summary>
/// The list of attachments that were in the MimeMessage.
/// </summary>
public IList<MimeEntity> Attachments {
get { return attachments; }
}

/// <summary>
/// The HTML string that can be set on the BrowserControl.
/// </summary>
public string HtmlBody {
get { return body ?? string.Empty; }
}

protected override void VisitMultipartAlternative (MultipartAlternative alternative)
{
// walk the multipart/alternative children backwards from greatest level of faithfulness to the least faithful
for (int i = alternative.Count - 1; i >= 0 && body == null; i--)
alternative[i].Accept (this);
}

protected override void VisitMultipartRelated (MultipartRelated related)
{
var root = related.Root;

// push this multipart/related onto our stack
stack.Add (related);

// visit the root document
root.Accept (this);

// pop this multipart/related off our stack
stack.RemoveAt (stack.Count - 1);
}

// look up the image based on the img src url within our multipart/related stack
bool TryGetImage (string url, out MimePart image)
{
UriKind kind;
int index;
Uri uri;

if (Uri.IsWellFormedUriString (url, UriKind.Absolute))
kind = UriKind.Absolute;
else if (Uri.IsWellFormedUriString (url, UriKind.Relative))
kind = UriKind.Relative;
else
kind = UriKind.RelativeOrAbsolute;

try {
uri = new Uri (url, kind);
} catch {
image = null;
return false;
}

for (int i = stack.Count - 1; i >= 0; i--) {
if ((index = stack[i].IndexOf (uri)) == -1)
continue;

image = stack[i][index] as MimePart;
return image != null;
}

image = null;

return false;
}

/// <summary>
/// Gets the attachent content as a data URI.
/// </summary>
/// <returns>The data URI.</returns>
/// <param name="attachment">The attachment.</param>
string GetDataUri (MimePart attachment)
{
using (var memory = new MemoryStream ()) {
attachment.Content.DecodeTo (memory);
var buffer = memory.GetBuffer ();
var length = (int) memory.Length;
var base64 = Convert.ToBase64String (buffer, 0, length);

return string.Format ("data:{0};base64,{1}", attachment.ContentType.MimeType, base64);
}
}

// Replaces <img src=...> urls that refer to images embedded within the message with
// "file://" urls that the browser control will actually be able to load.
void HtmlTagCallback (HtmlTagContext ctx, HtmlWriter htmlWriter)
{
if (ctx.TagId == HtmlTagId.Image && !ctx.IsEndTag && stack.Count > 0) {
ctx.WriteTag (htmlWriter, false);

// replace the src attribute with a file:// URL
foreach (var attribute in ctx.Attributes) {
if (attribute.Id == HtmlAttributeId.Src) {
MimePart image;
string url;

if (!TryGetImage (attribute.Value, out image)) {
htmlWriter.WriteAttribute (attribute);
continue;
}

url = GetDataUri (image);

htmlWriter.WriteAttributeName (attribute.Name);
htmlWriter.WriteAttributeValue (url);
} else {
htmlWriter.WriteAttribute (attribute);
}
}
} else if (ctx.TagId == HtmlTagId.Body && !ctx.IsEndTag) {
ctx.WriteTag (htmlWriter, false);

// add and/or replace oncontextmenu="return false;"
foreach (var attribute in ctx.Attributes) {
if (attribute.Name.ToLowerInvariant () == "oncontextmenu")
continue;

htmlWriter.WriteAttribute (attribute);
}

htmlWriter.WriteAttribute ("oncontextmenu", "return false;");
} else {
// pass the tag through to the output
ctx.WriteTag (htmlWriter, true);
}
}

protected override void VisitTextPart (TextPart entity)
{
TextConverter converter;

if (body != null) {
// since we've already found the body, treat this as an attachment
attachments.Add (entity);
return;
}

if (entity.IsHtml) {
converter = new HtmlToHtml {
HtmlTagCallback = HtmlTagCallback
};
} else if (entity.IsFlowed) {
var flowed = new FlowedToHtml ();
string delsp;

if (entity.ContentType.Parameters.TryGetValue ("delsp", out delsp))
flowed.DeleteSpace = delsp.ToLowerInvariant () == "yes";

converter = flowed;
} else {
converter = new TextToHtml ();
}

body = converter.Convert (entity.Text);
}

protected override void VisitTnefPart (TnefPart entity)
{
// extract any attachments in the MS-TNEF part
attachments.AddRange (entity.ExtractAttachments ());
}

protected override void VisitMessagePart (MessagePart entity)
{
// treat message/rfc822 parts as attachments
attachments.Add (entity);
}

protected override void VisitMimePart (MimePart entity)
{
// realistically, if we've gotten this far, then we can treat this as an attachment
// even if the IsAttachment property is false.
attachments.Add (entity);
}
}
}

And the way to use the above code along with a System.Windows.Forms.WebBrowser is this:

void Render ()
{
var visitor = new HtmlPreviewVisitor ();
message.Accept (visitor);

webBrowser.DocumentText = visitor.HtmlBody;
}

What the HtmlPreviewVisitor does is to embed the image attachments into the HTML using the "data:" URL scheme in the <img> tags.

VB.NET Email with multiple embedded Images

It involves 2 loops.

1 loop at the point of creatng the body.

Call this from BodyText using: <%= CreateImages(imagePaths, hasCustImage) %>

Private Shared Function CreateImages(ByVal imagePaths As IList(Of String), ByVal hasCustImage As Boolean) As XElement

Dim images As XElement = <span></span>
Dim temp As XElement = Nothing

For i As Integer = 0 To imagePaths.Count - 1

Dim img As String = String.Format("cid:ItemImage{0}", i + 1)

If ((i = (imagePaths.Count - 1)) And (hasCustImage)) Then

temp = _
<p style="font-family: Arial; font-size: 10pt">
Customer:<br/>
<img src=<%= img %>/>
</p>

Else

temp = _
<p style="font-family: Arial; font-size: 10pt">
<img src=<%= img %>/>
</p>

End If

images.Add(temp)

Next

Return images

End Function

Another loop to create the Alternative views:

msg.Body = bodyText.ToString()

Dim htmlContent As AlternateView = AlternateView.CreateAlternateViewFromString(bodyText.ToString(), Nothing, mediaType)

For i As Integer = 0 To imagePaths.Count - 1

If (IO.File.Exists(imagePaths(i))) Then

Dim itemImage As New LinkedResource(imagePaths(i))

itemImage.ContentId = String.Format("ItemImage{0}", i + 1)
htmlContent.LinkedResources.Add(itemImage)

End If

Next

msg.AlternateViews.Add(htmlContent)

Return Utils.Send(msg)

Send e-mail with image on body

You could convert your image to base64 and add that in src or you could just give the page where is the image is located.

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

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);
}
}

Adding Image to System.Net.Mail Message

you need to add them in the email message as CID's/linked resources.

here is some code I have used before which works nicely. I hope this gives you some guidance:

Create an AlternateView:

AlternateView av = AlternateView.CreateAlternateViewFromString(body, null, isHTML ? System.Net.Mime.MediaTypeNames.Text.Html : System.Net.Mime.MediaTypeNames.Text.Plain)

Create a Linked Resource:

LinkedResource logo = new LinkedResource("SomeRandomValue", System.Net.Mime.MediaTypeNames.Image.Jpeg);
logo.ContentId = currentLinkedResource.Key;
logo.ContentType = new System.Net.Mime.ContentType("image/jpg");

// add it to the alternative view

av.LinkedResources.Add(logo);

// finally, add the alternative view to the message:

msg.AlternateView.Add(av);

here is some documentation to help you with what the AlternativeView and LinkedResources are and how it works:

http://msdn.microsoft.com/en-us/library/system.net.mail.mailmessage.alternateviews(v=vs.110).aspx
http://msdn.microsoft.com/en-us/library/system.net.mail.linkedresource(v=vs.110).aspx
http://msdn.microsoft.com/en-us/library/ms144669(v=vs.110).aspx

in the HTML itself, you need to do something like the following:

"<img style=\"width: 157px; height: 60px;\" alt=\"blah blah\" title=\"my title here\" src=\"cid:{0}\" />";

notice the CID followed by a string format {0} - I then use this to replace it with a random value.

UPDATE

To go back and comment on the posters comments... here is the working solution for the poster:

string body = "blah blah blah... body goes here with the image tag: <img src=\"cid:companyLogo\" width="104" height="27" />";

byte[] reader = File.ReadAllBytes("E:\\TestImage.jpg");
MemoryStream image1 = new MemoryStream(reader);
AlternateView av = AlternateView.CreateAlternateViewFromString(body, null, System.Net.Mime.MediaTypeNames.Text.Html);

LinkedResource headerImage = new LinkedResource(image1, System.Net.Mime.MediaTypeNames.Image.Jpeg);
headerImage.ContentId = "companyLogo";
headerImage.ContentType = new ContentType("image/jpg");
av.LinkedResources.Add(headerImage);

System.Net.Mail.MailMessage message = new System.Net.Mail.MailMessage();
message.AlternateViews.Add(av);
message.To.Add(emailTo);
message.Subject = " Your order is being processed...";
message.From = new System.Net.Mail.MailAddress("xxx@example.com");

ContentType mimeType = new System.Net.Mime.ContentType("text/html");
AlternateView alternate = AlternateView.CreateAlternateViewFromString(body, mimeType);
message.AlternateViews.Add(alternate);

// then send message!



Related Topics



Leave a reply



Submit