Exchange Web Services - Inline Images to outside Email Clients not rendering - c#

I am using EWS to receive/send emails. I am sending embedded images in the email to navigate users to another page. However, when sending to other emails clients like gmail and yahoo, the images are not being set as html, but text. I created a console app below to demonstrate:
static void Main(string[] args)
{
var service = getExchangeService(NetworkCreds.Credentials);
// Create the HTML body with the content identifier of the attachment.
string html = #"<html>
<head>
</head>
<body>
<img width=100 height=100 id=""1"" src=""cid:Party.jpg"">
</body>
</html>";
// Create the email message.
EmailMessage email = new EmailMessage(service);
email.Subject = "Test Email";
email.Body = new MessageBody(BodyType.HTML, html);
email.ToRecipients.Add("test#gmail.com");
// Add the attachment to the local copy of the email message.
string file = #"C:\projects\Party.jpg";
email.Attachments.AddFileAttachment("Party.jpg", file);
email.Attachments[0].IsInline = true;
email.Attachments[0].ContentId = "Party.jpg";
// Save a copy of the email, add the attachment, and then send the email. This method results in three calls to EWS.
email.SendAndSaveCopy();
}
When I receive the message in outlook it works fine but in gmail it only has the following text in the body: [cid:Party.jpg] with the file attachment below. I know Gmail and other clients have extensive processing of images and that may the case of why it's not displaying correctly. My question is if inline images can be sent from exchange web services to other clients and be processed correctly?

This is how you fix this kind of thing
bodyText = bodyText.Replace("cid:" + fileAttachment.ContentId, fileAttachment.Name);
Be sure to attach the file as "Image 001.png" or whatever it is, then you can resend this e-mail with images in tact :)

I know that i am a bit late but maybe someone else will stuck at a similar problem and this could help solving the problem.
You could set the image as base64 encoded image.
In my case i wanted to load a preview of an existing email in a browser control.
Everything worked except showing the images. But replacing them to base64 encoded strings worked perfectly.
In this example following code should work as described:
EmailMessage email = new EmailMessage(service);
email.Subject = "Test Email";
email.Body = new MessageBody(BodyType.HTML, html);
email.ToRecipients.Add("test#gmail.com");
// Add the attachment to the local copy of the email message.
string file = #"C:\projects\Party.jpg";
MemoryStream stream = new MemoryStream();
email.Attachments.AddFileAttachment("Party.jpg", file).Load(stream);
byte[] imageBytes = stream.ToArray();
// Convert byte[] to Base64 String
string base64String = Convert.ToBase64String(imageBytes);
stream.close();
email.Attachments[0].IsInline = true;
email.Attachments[0].ContentId = "Party.jpg";
string html = #"<html>
<head>
</head>
<body>
<img width=100 height=100 id=""1"" src=""data: image/jpg;base64, " + base64String + """>
</body>
</html>";

Related

Embed image in email with latest C# Sendgrid 9.28.1

I'm trying to embed an image in an email that's send with Sendgrid C# 9.28.1. But somehow the image is not displayed. There is also not a function anymore that's called EmbedImage. Is there a way to do this?
var client = new SendGridClient(_settings.ApiKey);
var myMessage = new SendGridMessage();
myMessage.AddTo(entity.Email);
myMessage.From = new EmailAddress(_settings.FromAddress, _settings.FromName);
myMessage.Subject = $"{_settings.FromName} - Image TEST";
var body = "<div style=\"width:200px;\"><img src=\"cid::IMAGE01\"/></div>" +
#"<p>Test message?</p>";
var attachment = new SendGrid.Helpers.Mail.Attachment
{
ContentId = "IMAGE01",
Content = entity.Base64ImageString,
Type = "image/png",
Filename = "image.png"
};
myMessage.AddAttachment(attachment);
myMessage.HtmlContent = body;
await client.SendEmailAsync(myMessage);
I was able to replicate your issue. When running your code I see the image attached, but not inlined into the body of the email.
To resolve the issue, I added the Disposition property and set it to "inline".
message.AddAttachment(new Attachment
{
ContentId = "IMAGE01",
Content = Convert.ToBase64String(File.ReadAllBytes("image.png")),
Type = "image/png",
Filename = "image.png",
Disposition = "inline" // important!
});
Once I added the property, the image was displayed inline. (I also use a single : instead of two : in the HTML, so cid: instead of cid:: but not sure it matters.
Here's what part of my email source looked like without the Disposition property:
<html>
<body>
<img src=3D"cid:IMAGE01"/>
</body>
</html>
--d0e309773e8692891fcbf3a6dc18894582dea28d62adfe2bd8fd84a96b90
Content-Disposition: attachment; filename="image.png"
Content-Transfer-Encoding: base64
Content-Type: image/png; name="image.png"
Notice how IMAGE01 only appears in the HTML, but not in the attachment.
This is with the Disposition property:
<html>
<body>
<img src=3D"cid:IMAGE01"/>
</body>
</html>
--5bb4b7a345af921dc9094ee0d66495c2a390a8aa6e89c2a41d96f8af8af5
Content-Disposition: inline; filename="image.png"
Content-ID: <IMAGE01>
Content-Transfer-Encoding: base64
Content-Type: image/png; name="image.png"
Notice how there's now a Content-ID: <IMAGE01> parameter.
Having said that, there are also two alternatives, you can use a data URL to embed the base64 data into the HTML, or you can upload the image to the internet and reference it.
You can find the source code here: https://github.com/Swimburger/SendGridImages/blob/main/Program.cs

How to remove image as attachment but show in body of email

I found this solution for showing an image in the body of the email:
Add image to body of an email
And it works fine but it also adds the image as an attachment to the email.
Attachment inlineLogo = new Attachment(EmailLogo.ImageUrl);
mailMsg.Attachments.Add(inlineLogo);
string contentID = "Image";
inlineLogo.ContentId = contentID;
//To make the image display as inline and not as attachment
inlineLogo.ContentDisposition.Inline = true;
inlineLogo.ContentDisposition.DispositionType = DispositionTypeNames.Inline;
//To embed image in email
mailMsg.Body = "<htm><body> <img height=\"49\" width=\"169\" src=\"cid:" + contentID + "\"> </body></html>";
There is a line of code with the comment to display as inline and not as attachment but this line isn't working because the image still gets attached to the email:
//To make the image display as inline and not as attachment
inlineLogo.ContentDisposition.Inline = true;
inlineLogo.ContentDisposition.DispositionType = DispositionTypeNames.Inline;
How can I stop the image from attaching to the email?
Use AlternateView to store your html code with image embedded as LinkedResource:
string contentID = "Image";
var inlineLogo = new LinkedResource(EmailLogo.ImageUrl, "image/png");
inlineLogo.ContentId = contentID;
var htmlView = AlternateView.CreateAlternateViewFromString(
"<html><body> <img height=\"30\" width=\"30\" src=\"cid:" + contentID + "\"> </body></html>",
Encoding.UTF8, "text/html");
htmlView.TransferEncoding = TransferEncoding.QuotedPrintable;
htmlView.LinkedResources.Add(inlineLogo);
mailMsg.AlternateViews.Add(htmlView);
P.S. Embedding image as base24 string is not very good idea, because many mail clients do not support such ability.
If you want to display an image in an email it has to exist somewhere. It is either attached as part of the message payload (regardless of whether it is "displayed inline" or as a true "attachment") - or is fetched from a remote web server when the reader reads the email (and optionally has to choose to "view images")
To not attach the image to the email payload itself:
You have to host the image on a public web server so that the reader opening the message can access it.
You have to use a fully qualified URL in your message body source, so it can find it.
Assuming you have stored the image on your web server at the following URL:
http://www.example.com/images/myimage.jpg
... then your source should simply change to reflect:
mailMsg.Body = "<html><body> <img height=\"49\" width=\"169\" src=\"http://www.example.com/images/myimage.jpg\"> </body></html>";
No need to attach it at all.
An alternative that can be used which embeds the image inline, but also isnt generally filtered by email clients is (which is generally the case today in things like junk mail) You could use a DataURL.
<img src="data:image/<type>;base64,<string>"/>
where <type> is the image type, ie jpg, gif,png, and is a base64 string. Just convert the image to a base64 string and assign it to the source using the above syntax
For example, with a jpeg...
mailMsg.Body = "<html><body> <img height=\"49\" width=\"169\" src=\"data:image/jpg;base64,<myPictureString>"\"> </body></html>";

How to use razor engine for email templating with image src

I've found this link on how to using Razor Engine for email templates in asp.net and it worked great. But I've tried to have a logo in the email template with an image.
Something like this:
EmailTemplate.cshtml (this by the way is a strongly-type view)
<html>
<body>
<img src="logo.jpg" />
</body>
</html>
and when I try to submit it on email, it seems that the image path was not read, it only rendered an X in the content.
I'm thinking to pass the image path as part of the Model but it seems odd that way. Is there any way to achieve this?
Any help would be much appreciated. Thanks
To see image everywhere you can use these options:
Absolute Url
You can simply use full absolute path of image for example "http://example.com/images/logo.png"
IMO It is the most simple option and recommended for your problem.
Attachment
As mentioned by Mason in comments You can attach image to mail and then put image tag and useContentId of attachment:
//(Thanks to Mason for comment and Thanks to Bartosz Kosarzyck for sample code)
string subject = "Subject";
string body = #"<img src=""$CONTENTID$""/> <br/> Some Content";
MailMessage mail = new MailMessage();
mail.From = new MailAddress("from#example.com");
mail.To.Add(new MailAddress("to#example.com"));
mail.Subject = subject;
mail.Body = body;
mail.Priority = MailPriority.Normal;
string contentID = Guid.NewGuid().ToString().Replace("-", "");
body = body.Replace("$CONTENTID$", "cid:" + contentID);
AlternateView htmlView = AlternateView.CreateAlternateViewFromString(body, null, "text/html");
//path of image or stream
LinkedResource imagelink = new LinkedResource(#"C:\Users\R.Aghaei\Desktop\outlook.png", "image/png");
imagelink.ContentId = contentID;
imagelink.TransferEncoding = System.Net.Mime.TransferEncoding.Base64;
htmlView.LinkedResources.Add(imagelink);
mail.AlternateViews.Add(htmlView);
SmtpClient client = new SmtpClient();
client.Host = "mail.example.com";
client.Credentials = new NetworkCredential("from#example.com", "password");
client.Send(mail);
Data Uri
you can use data uri (data:image/png;base64,....).
Not Recommended because of weak support in most of mail clients, I tested it with Outlook.com(web) and OutlookWebAccess(web) and Office Outlook(Windows) and Outlook(windows 8.1) and unfortunately it worked only on OutlookWebAccess(web).

Get file to send as attachment from byte array

I have an ASP.NET MVC application that has to send an email to a list of recipients with an attachment detailing a specific "Project". I get the details for this from a report by making use of SQL Server Reporting Services (SSRS).
I've never really used SSRS before, but I received some code from a colleague where he used it. What he does with the report is he downloads it in the browser to the client's machine. So, if I don't do that I sit with a byte array containing the report data.
Is there a way I can send this as an attachment without first physically writing the file to the filesystem of the server? The report will either be in excel format or a pdf.
Edit: I am using SmtpClient to send the email's.
Get the file data in a byte[]
byte[] binaryFile = // get your file data from the SSRS ...
string filename = "SSRS.pdf";
Prepare a list or array of the destination addresses:
string[] addresses = // get addresses somehow (db/hardcoded/config/...)
sending smtp message through SmtpClient:
MailMessage mailMessage= new MailMessage();
mailMessage.From = new MailAddress("sender email address goes here");
// Loop all your clients addresses
foreach (string address in addresses)
{
mailMessage.To.Add(address);
}
mailMessage.Subject = "your message subject goes here";
mailMessage.Body = "your message body goes here";
MemoryStream memoryStream = new MemoryStream(binaryFile);
mailMessage.Attachments.Add( new Attachment( memoryStream, filename , MediaTypeNames.Application.Pdf ));
SmtpClient smtpClient = new SmtpClient();
smtpClient.Send(mailMessage);
to do this you would need to leverage off the SSRS ReportManager API as follows.
First read in the report from the Web Service with SSRS
Read the file into memory rather than saving to the server or client
Send the MemoryStream object straight to the email server.
Reporting services: Get the PDF of a generated report
How to send an email with attachments using SmtpClient.SendAsync?
string strReportUser = "RSUserName";
string strReportUserPW = "MySecretPassword";
string strReportUserDomain = "DomainName";
string sTargetURL = "http://SqlServer/ReportServer?" +
"/MyReportFolder/Report1&rs:Command=Render&rs:format=PDF&ReportParam=" +
ParamValue;
HttpWebRequest req =
(HttpWebRequest)WebRequest.Create( sTargetURL );
req.PreAuthenticate = true;
req.Credentials = new System.Net.NetworkCredential(
strReportUser,
strReportUserPW,
strReportUserDomain );
HttpWebResponse HttpWResp = (HttpWebResponse)req.GetResponse();
Stream fStream = HttpWResp.GetResponseStream();
HttpWResp.Close();
//Now turn around and send this as the response..
ReadFullyAndSend( fStream );
ReadFullyAnd send method.
NB: the SendAsync call so your not waiting for the server to send the email completely before you are brining the user back out of the land of nod.
public static void ReadFullyAndSend( Stream input )
{
using ( MemoryStream ms = new MemoryStream() )
{
input.CopyTo( ms );
MailMessage message = new MailMessage("from#foo.com", "too#foo.com");
Attachment attachment = new Attachment(ms, "my attachment",, "application/vnd.ms-excel");
message.Attachments.Add(attachment);
message.Body = "This is an async test.";
SmtpClient smtp = new SmtpClient("localhost");
smtp.Credentials = new NetworkCredential("foo", "bar");
smtp.SendAsync(message, null);
}
}

How to send multi-part MIME messages in c#?

I want to send multi-part MIME messages with an HTML component plus a plain text component for people whose email clients can't handle HTML. The System.Net.Mail.MailMessage class doesn't appear to support this. How do you do it?
D'oh, this is really simple... but I'll leave the answer here for anyone who, like me, came looking on SO for the answer before Googling... :)
Credit to this article.
Use AlternateViews, like so:
//create the mail message
var mail = new MailMessage();
//set the addresses
mail.From = new MailAddress("me#mycompany.com");
mail.To.Add("you#yourcompany.com");
//set the content
mail.Subject = "This is an email";
//first we create the Plain Text part
var plainView = AlternateView.CreateAlternateViewFromString("This is my plain text content, viewable by those clients that don't support html", null, "text/plain");
//then we create the Html part
var htmlView = AlternateView.CreateAlternateViewFromString("<b>this is bold text, and viewable by those mail clients that support html</b>", null, "text/html");
mail.AlternateViews.Add(plainView);
mail.AlternateViews.Add(htmlView);
//send the message
var smtp = new SmtpClient("127.0.0.1"); //specify the mail server address
smtp.Send(mail);

Categories

Resources