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
How to Get the Index of the Highest Value in an Array Using Linq
How to Get the Path of the Current User's "Application Data" Folder
How to Get MAC Address of Client That Browse Web Site by ASP.NET MVC C#
Changing Font for Richtextbox Without Losing Formatting
Get Values from Process Standardoutput
Http Post Returns Error: 417 "Expectation Failed."
In C# Differencebetween a Destructor and a Finalize Method in a Class
Dbarithmeticexpression Arguments Must Have a Numeric Common Type
How to Ignore a Property When Serializing Using the Datacontractserializer
ASP.NET MVC 3: Override "Name" Attribute with Textboxfor
PDF Compression with Itextsharp
Converting ASP.NET MVC Razor @Helper Function into a Method of a Helper Class
How to Draw Shapes in Winforms
Could Not Load File or Assembly 'System.Web.Http 4.0.0 After Update from 2012 to 2013
Event Signature in .Net -- Using a Strong Typed 'Sender'
How to Write to Console.Out During Execution of an Mstest Test