Embed c# image into html email - c#

I have a class that returns an Image:
public static Image GenerateVoucher(decimal voucherAmount, string voucherCode,
DateTime expiryDate)
{
...
Image img = Image.FromFile(path);
using (Graphics graphics = Graphics.FromImage(img))
{
...various drawing on the image...
graphics.DrawString(voucherAmount.ToString(), new Font(handleGothic.Families[0], 113), blueBrushColour, largeVoucherAmountLocation);
}
return img;
}
and I want to embed this response into an email:
public static MailMessage CreateImagedEmail(string mailTo, decimal voucherAmount, string voucherCode, DateTime expiryDate)
{
var mail = new MailMessage();
mail.From = new MailAddress("fromaddress.com");
mail.To.Add(mailTo);
mail.Subject = "Your Email with an image inline";
var plainView = AlternateView.CreateAlternateViewFromString(
"Your email client has detected it is unable to render html emails...",
null, "text/plain");
var htmlView = AlternateView.CreateAlternateViewFromString(
"<h1>Some HTML message </h1>" +
"<img src=cid:voucher>",
null, "text/html");
var voucher = GenerateVoucher(voucherAmount, voucherCode, expiryDate);
LinkedResource voucherImg = new LinkedResource(path);
voucherImg.ContentId = "voucher";
htmlView.LinkedResources.Add(voucherImg);
mail.AlternateViews.Add(plainView);
mail.AlternateViews.Add(htmlView);
return mail;
}
This works fine in the example above when returning the image from the file system, but when I try and add the Image from the method above the LinkedResources complains that it is not a string. I'm happy to convert it to a stream if that will work

Related

LinkedResource fails in MailMessage with CC recipients, succeeds otherwise

I am developing a Windows Background Service with .NET and C#. It periodically queries a REST API and sends emails if certain conditions are met. Sometimes the data returned from the API will have image files and sometimes not. When image files are returned, the service embeds them into the email body before sending. I'm embedding the image file in four steps:
Download the image as a byte[]
Convert byte[] to MemoryStream
pass the stream to a new LinkedResource
Add the linked resource to the AlternateView for the MailMessage
This works if the message has a single "To" recipient and no CC or BCC recipients. The image displays correctly in the body of the email. The weird problem I'm having is that if I add multiple recipients in To, CC or BCC, the image fails to display in the email body. This problem seems to be limited to Outlook. I did a test with two recipients, one internal to the organization and one Gmail account. The image displayed correctly in Gmail but was broken in Outlook.
Here is the snippet of code where the service builds the LinkedResource and sends the email.
htmlBody = htmlBody.Replace("{incident_time}", incident.IncidentTime)
.Replace("{incident_type}", incident.IncidentType)
.Replace("{incident_location}", incident.IncidentLocation)
.Replace("{reporting_agency}", incident.ReportingAgency)
.Replace("{severity}", incident.Severity)
.Replace("{details}", incident.Details);
// embed attached images
var attachmentsResult = await arcGisRestClient.QueryAttachmentsAsync(
incidentSublayerUrl,
tokenResponse.Token,
objectId: incident.ObjectId);
IList<LinkedResource> linkedResources = new List<LinkedResource>();
if (!string.IsNullOrEmpty(attachmentsResult))
{
var attachmentsObj = JObject.Parse(attachmentsResult);
var attachments = FeatureAttachmentConverter.FromJObject(attachmentsObj);
// if attachments > 0
if (attachments.Count > 0)
{
string imgHtml = "";
foreach (var attachment in attachments)
{
string attachmentUrl = $"{incidentSublayerUrl}/{attachment.ParentObjectId}/attachments/{attachment.Id}?token={tokenResponse.Token}";
WebClient webClient = new WebClient();
byte[] bytes = webClient.DownloadData(attachmentUrl);
MemoryStream stream = new MemoryStream(bytes);
LinkedResource linkedResource = new LinkedResource(stream, MediaTypeNames.Image.Jpeg);
imgHtml += #"<img src='cid:" + linkedResource.ContentId + #"' width='450'/>";
linkedResources.Add(linkedResource);
}
htmlBody = htmlBody.Replace("{attachments}", imgHtml);
}
else
{
htmlBody = htmlBody.Replace("{attachments}", "");
}
}
AlternateView htmlAlternate = AlternateView.CreateAlternateViewFromString(htmlBody, new ContentType(MediaTypeNames.Text.Html));
foreach (var lr in linkedResources)
{
htmlAlternate.LinkedResources.Add(lr);
}
// create email
MailMessage message = new MailMessage
{
Subject = "TEST Incident Notification",
};
message.AlternateViews.Add(htmlAlternate);
string[] emailToAddresses = _config.GetSection("EmailNotificationTo").Get<string[]>();
message.To.Add(string.Join(',', emailToAddresses));
string[] emailCcAddresses = _config.GetSection("EmailNotificationCc").Get<string[]>();
message.CC.Add(string.Join(',', emailCcAddresses));
message.From = new MailAddress(_config["EmailNotificationFrom"]);
// send email
SmtpClient smtpClient = new SmtpClient("mail.domain.org", 25);
using (smtpClient)
{
smtpClient.Send(message);
logger.Info($"Email notification sent; objectid: {incident.ObjectId}");
}
Could this be an issue with our internal STMP server or Outlook settings rather than a problem with the above code?

Changing name of automatic attached file in e-mail

I am using Mailkit library to send e-mails. This is the code to do so:
public async Task SendAsync(IdentityMessage message)
{
if (message == null)
return;
LinkedResource inline = new LinkedResource(System.Web.Hosting.HostingEnvironment.MapPath($"~/Images/logo.png"))
{
ContentId = Guid.NewGuid().ToString(),
ContentType = new ContentType("image/png")
{
Name = "logo.png"
}
};
var htmlBody = message.Body.Replace("{CID:LOGO}", String.Format("cid:{0}", inline.ContentId));
AlternateView avHtml = AlternateView.CreateAlternateViewFromString(htmlBody, null, MediaTypeNames.Text.Html);
avHtml.LinkedResources.Add(inline);
using (MailMessage objMailMsg = new MailMessage())
{
objMailMsg.AlternateViews.Add(avHtml);
objMailMsg.Body = htmlBody;
objMailMsg.Subject = message.Subject;
objMailMsg.BodyEncoding = Encoding.UTF8;
Properties.Settings.Default.Reload();
string fromEmail = Properties.Settings.Default.FromMail;
string fromName = Properties.Settings.Default.FromName;
string smtpPassword = Properties.Settings.Default.PwdSmtp;
objMailMsg.From = new MailAddress(fromEmail, fromName);
objMailMsg.To.Add(message.Destination);
objMailMsg.IsBodyHtml = true;
MimeMessage mime = MimeMessage.CreateFromMailMessage(objMailMsg);
HeaderId[] headersToSign = new HeaderId[] { HeaderId.From, HeaderId.Subject, HeaderId.Date };
string domain = "example.cl";
string selector = "default";
DkimSigner signer = new DkimSigner(#"C:\inetpub\dkim.pem", domain, selector)
{
HeaderCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Relaxed,
BodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple,
AgentOrUserIdentifier = "#contact.example.cl",
QueryMethod = "dns/txt",
};
mime.Prepare(EncodingConstraint.EightBit);
signer.Sign(mime, headersToSign);
using (SmtpClient smtpClient = new SmtpClient())
{
await Task.Run(() =>
{
try
{
smtpClient.Connect(Properties.Settings.Default.ServidorSmtp, Properties.Settings.Default.PuertoSmtp, Properties.Settings.Default.SSLSmtp);
smtpClient.Authenticate(fromEmail, smtpPassword);
smtpClient.Send(mime);
smtpClient.Disconnect(true);
}
catch (Exception ex)
{
ErrorLog.Save(ex);
//InfoLog.Save(htmlBody);
}
});
}
}
}
Well.. when the e-mail arrives, e-mail client shows it correctly in HTML format. However, an HTML file is attached in the e-mail also.
That file is named, for example, Attached data without title 00391.html. If I open that file in browser, the e-mail body is shown.
I have not found a way to change the name of that attachment.
Does anyone know?
The problem is probably this:
var htmlBody = message.Body.Replace("{CID:LOGO}", String.Format("cid:{0}", inline.ContentId));
AlternateView avHtml = AlternateView.CreateAlternateViewFromString(htmlBody, null, MediaTypeNames.Text.Html);
// ...
objMailMsg.AlternateViews.Add(avHtml);
objMailMsg.Body = htmlBody;
You are setting the HTML body text as 2 different body parts. If you create the avHtml part, you shouldn't also need to set objMailMsg.Body = htmlBody.
When MailKit later converts the System.Net.Mail.MailMessage into a MimeMessage, it probably ends up with 2 duplicate parts, one that gets used as the actual HTML body and one that gets added as an attachment w/o a name (the receiving client likely assigns this part a random name which is what you are seeing).
Alternatively, you could remove the logic that creates a System.Net.Mail.MailMessage completely and just construct a MimeMessage and avoid potential issues in conversion.
var bodyBuilder = new BodyBuilder ();
var logo = bodyBuilder.LinkedResources.Add (System.Web.Hosting.HostingEnvironment.MapPath($"~/Images/logo.png"));
var htmlBody = message.Body.Replace("{CID:LOGO}", String.Format("cid:{0}", logo.ContentId));
bodyBuilder.HtmlBody = htmlBody;
Properties.Settings.Default.Reload();
string fromEmail = Properties.Settings.Default.FromMail;
string fromName = Properties.Settings.Default.FromName;
string smtpPassword = Properties.Settings.Default.PwdSmtp;
var mime = new MimeMessage ();
mime.From.Add (new MailboxAddress (fromName, fromEmail));
mime.To.Add (MailboxAddress.Parse (message.Destination));
mime.Subject = message.Subject;
mime.Body = bodyBuilder.ToMessageBody ();
HeaderId[] headersToSign = new HeaderId[] { HeaderId.From, HeaderId.Subject, HeaderId.Date };
string domain = "example.cl";
string selector = "default";
DkimSigner signer = new DkimSigner(#"C:\inetpub\dkim.pem", domain, selector)
{
HeaderCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Relaxed,
BodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple,
AgentOrUserIdentifier = "#contact.example.cl",
QueryMethod = "dns/txt",
};
mime.Prepare(EncodingConstraint.EightBit);
signer.Sign(mime, headersToSign);

Send an Embed Base64 Image in an email with C# in order to be decoded by gmail

I am trying to send an email which has embedded images in the body of the message.
The images are encoded as Base64 string. The contents looks as follows:
"<p><span>My Name</span></p><img src=\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEA..." width=\"167\" height=\"167\" />"
The base64 string has been cut off for this example.
I am using the following code to send the html as attachment, However, the image in the example is still been refused to be decoded by the emails providers (gmail/outlook). I do not know if i am not doing it properly.
string htmlBody = "<p><span>My Name</span></p><img src=\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEA..." width=\"167\" height=\"167\" />";
AlternateView avHtml = AlternateView.CreateAlternateViewFromString
(htmlBody, null, MediaTypeNames.Text.Html);
var mailTo = Config.Debug ? new MailAddress(Config.DebugEmailAddress) : new MailAddress("myemail#gmail.com");
var mailFrom = new MailAddress("myemail#gmail.com");
var mailMessage = new MailMessage(mailFrom, mailTo) { Subject = "hola q tal", Body = "holaaa", IsBodyHtml = true };
mailMessage.To.Add("myemail#gmail.com");
mailMessage.AlternateViews.Add(avHtml);
var sender1 = new SmtpClient
{
Host = Config.SmtpHost,
Port = 25,
Credentials = new NetworkCredential(Config.SmtpHostUserName, Config.SmtpHostPassword)
};
sender1.Send(mailMessage);
I would appreciate any sugesstions.

linked resource does not come as inline image C#

I have written below code to embed image in the mail which is being sent from my c# code. But when i check the mail, i get image like an attachment and not as an inline image. (Gmail)
AlternateView htmlBodyView = null;
string htmlBody = "<html><body><h1></h1><br><img src=\"cid:SampleImage\"></body></html>";
AlternateView plainTextView = AlternateView.CreateAlternateViewFromString(htmlBody, null, "text/html");
ImageConverter ic = new ImageConverter();
Byte[] ba = (Byte[])ic.ConvertTo(bitmap_obj, typeof(Byte[]));
using (MemoryStream logo = new MemoryStream(ba))
{
LinkedResource sampleImage = new LinkedResource(logo, "image/jpeg");
sampleImage.ContentId = "sampleImage";
htmlBodyView.LinkedResources.Add(sampleImage);
p.SendEmail(htmlBodyView);
}
For reference, a full working simplified example:
public void SendMail()
{
LinkedResource logo = new LinkedResource(
"images\\image005.png", //Path of file
"image/png"); //Mime type: Important!
logo.ContentId = "logo"; //ID for reference
//Actual HTML content of the body in an 'AlternateView' object.
AlternateView vw = AlternateView.CreateAlternateViewFromString(
"Hello, this is <b>HTML</b> mail with embedded image: <img src=\"cid:logo\" />",
null,
MediaTypeNames.Text.Html); //Mime type: again important!
vw.LinkedResources.Add(logo);
var msg = new MailMessage() { IsBodyHtml = true };
msg.AlternateViews.Add(vw);
msg.From = new MailAddress("sender#domain.com");
msg.To.Add(new MailAddress("reciever#domain.com"));
msg.Subject = "HTML Mail!";
using (var client = new SmtpClient("localhost", 25))
client.Send(msg);
}

C# LinkedResource using base64 string

How can I put this ==>
url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAgEASABIAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB') into new System.Net.Mail.LinkedResource()
to send mail form C#, using background css style with base64 string, not file url.
I wondered this myself and got to this post. I solved it and figured i would share the my solution.
var imageData = Convert.FromBase64String("/9j/4AAQSkZJRgABAgEASABIAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB");
var contentId = Guid.NewGuid().ToString();
var linkedResource = new LinkedResource(new MemoryStream(imageData), "image/jpeg");
linkedResource.ContentId = contentId;
linkedResource.TransferEncoding = TransferEncoding.Base64;
var body = string.Format("<img src=\"cid:{0}\" />", contentId);
var htmlView = AlternateView.CreateAlternateViewFromString(body, null, "text/html");
htmlView.LinkedResources.Add(linkedResource);

Categories