Send images using sendgrid C# - c#

I am using sendgrid to send images in mails, but I am not sure what's wrong with my code.
Here is my code:
string imgPath= Server.MapPath(#"~/Images/logo-img.png");
StringBuilder sb = new StringBuilder();
sb.Append("Hi There! I am using sendgrid to send images");
sb.Append("<a href='someurl'><img src='cid:myImage.png> Login in</a>");
LinkedResource lr = new LinkedResource(imgPath, "image/png");
lr.ContentId = "myImage";
AlternateView AV = AlternateView.CreateAlternateViewFromString(sb.ToString(), null, "image/png");
AV.LinkedResources.Add(lr);
var from = new EmailAddress("abc#bac.com");
var subject = "Hi There!";
var to = new EmailAddress("xyz#xyz.com");
var plainTextContent = "Hello";
var htmlContent = sb.ToString();
var msg = MailHelper.CreateSingleEmail(from, to, subject, plainTextContent, htmlContent);
msg.SetFrom(from, "abc");
var sendingResult = SendMail(msg);

You put quote in wrong place:
sb.Append("<a href='someurl'><img src='cid:myImage.png'> Login in</a>");

I don't know what the LinkedResource or AlternateView classes do, but they don't appear to be part of the Twilio SendGrid C# library.
There is an example in the library docs for sending attachments, but it doesn't show how to address the content within the email. But it's close. The key is that you need to load the file you want to attach and transform it to base64, then use the AddAttachment method to add it as an inline attachment to your mail, passing the content ID (cid) that you can refer to in the body of the message. Something like:
string imgPath= Server.MapPath(#"~/Images/logo-img.png");
StringBuilder sb = new StringBuilder();
sb.Append("Hi There! I am using sendgrid to send images");
sb.Append("<a href='someurl'><img src='cid:myImage'> Login in</a>");
var bytes = File.ReadAllBytes(imgPath);
var file = Convert.ToBase64String(bytes);
var from = new EmailAddress("abc#bac.com");
var subject = "Hi There!";
var to = new EmailAddress("xyz#xyz.com");
var plainTextContent = "Hello";
var htmlContent = sb.ToString();
var msg = MailHelper.CreateSingleEmail(from, to, subject, plainTextContent, htmlContent);
msg.SetFrom(from, "abc");
msg.AddAttachment("logo-img.png", file, "image/png", "inline", "myImage");
var sendingResult = SendMail(msg);
In this updated code the important parts are loading the file as base64:
var bytes = File.ReadAllBytes(imgPath);
var file = Convert.ToBase64String(bytes);
and attaching the file to the message:
msg.AddAttachment("logo-img.png", file, "image/png", "inline", "myImage");
The AddAttachment method takes 5 arguements: the filename, the base64 content of the file, the mime type, the content disposition (for referring to images in the HTML you should use "inline"), and the cid. Note also that the cid is the entire string you should use to refer to the image, so if the image cid is "myImage" then you should refer to it as cid:myImage, like:
sb.Append("<a href='someurl'><img src='cid:myImage'> Login in</a>");

Related

S/Mime with attachment sign using MailSystem.Net

I'm new to s/mime and need to digitally sign email with xml attachment, but unfortunately this email has a wrong hash value (according to response of external system). I digged into the code of the library and found that it creates a sign for base64-encoded body part, is it correct or the signature should be computed for xml attachment content?
Also here is some more issues:
Lots of headers/parameters are owerritten by library: for ex. ContentType parameters, some headers (like X-Mailer) and many others
It creates an empty boundary for Content-Type: text/plain, though I haven't any text except attachment
Here is my code:
public static void Sign(X509Certificate2 clientCert, string from, string to, string subject, string attachementPath)
{
Message message = new Message();
message.From = new Address(from);
message.To.Add(to);
message.ContentType.MimeType = "multipart/signed";
message.ContentType.Parameters.Add("protocol", "\"application/pkcs7-signature\"");
message.ContentTransferEncoding = ContentTransferEncoding.SevenBits;
message.AddHeaderField("MIME-Version", "1.0");
message.Subject = subject;
var mimePart = new MimePart(attachementPath, false);
mimePart.ContentTransferEncoding = ContentTransferEncoding.Base64;
mimePart.Charset = "windows-1251";
mimePart.ContentType.MimeType = "text/xml";
message.Attachments.Add(mimePart);
message.BuildMimePartTree();
CmsSigner signer = new CmsSigner(clientCert);
signer.IncludeOption = X509IncludeOption.EndCertOnly;
message.SmimeAttachSignatureBy(signer);
}

Sending MHT file using C# Mail Message

Has anyone got any sample code to send MHT using SmtpClient and MailMessage in C#?
I have found tantalising references to using Alternate Views but I cannot figure out how to make this work with my SSRS generated MHT.
If anyone has any working code they are willing to share I would be greatly appreciative.
So it appears that .NET MailMessage does not support this out of the box. I have found a couple of approaches that can be used with varying degrees of success.
MHTML -> HTML Decoder
David Benko wrote https://github.com/DavidBenko/MHTML-to-HTML-Decoding-in-C-Sharp/blob/master/MHTMLParser.cs and while it works, I was unable to make the images appear when I viewed the resulting email. The HTML looked right, it just did not work in Outlook 2010.
Convert MimeKit MimeMessage to .NET Mail Message
As Jeffrey points out this is a start https://github.com/jstedfast/MimeKit/issues/140. I had problems again with the images not being displayed in the resulting message.
Use MimeKit & MailKit
I ended up using https://github.com/jstedfast/MimeKit to do the job. This can be achieved as follows:
MimeMessage messageMimeKit = MimeMessage.Load(Request.MapPath("~/Blah/378921.mht"));
messageMimeKit.From.Add(new MailboxAddress("Dev", "developer#gmail.com"));
messageMimeKit.To.Add(new MailboxAddress("Homer", "homer#gmail.com"));
messageMimeKit.Subject = "Another subject line";
using (var client = new MailKit.Net.Smtp.SmtpClient())
{
client.Connect("smtp.gmail.com", 465, true);
client.Authenticate("homer#gmail.com", "*****");
client.Send(messageMimeKit);
client.Disconnect(true);
}
Using System.Net.Http.Formatting
My approach leverages the ReadAsMultipartAsync extension methods on HttpContent found in the System.Net.Http.Formatting library. (part of the Microsoft.AspNet.WebApi.Client nuget package)
The only tricky bit is getting the correct content type out of the file. In order for the ReadAsMultipartAsync method to work, it needs the ContentType header on the stream content set to the correct type ("multipart/related"), including the correct boundary parameter on the content type value:
multipart/related;
boundary="----=_NextPart_01C35DB7.4B204430"
Please note, since my solution was only written for sending content from SSRS reports exported in the MTHML format, my code assumes the multipart content has only one text/html part with the remaining parts being images, but the technique is sound and could be adjusted to handle multipart content in a more generic way.
const string ContentTypePrefix = "Content-Type: ";
AlternateView htmlView = null;
var linkedResources = new List<LinkedResource>();
var content = File.ReadAllText("report.mhtml");
var startIndex = content.IndexOf(ContentTypePrefix, StringComparison.OrdinalIgnoreCase) + ContentTypePrefix.Length;
var endIndex = content.IndexOf("\"", startIndex, StringComparison.Ordinal) + 1;
var contentType = content.Substring(startIndex, endIndex);
using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(content)))
{
using (var stream = new StreamContent(memoryStream))
{
stream.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType);
if (!stream.IsMimeMultipartContent())
{
throw new Exception("Not correct format.");
}
var parts = stream.ReadAsMultipartAsync().ConfigureAwait(false).GetAwaiter().GetResult();
foreach (var part in parts.Contents)
{
part.Headers.ContentType.CharSet = part.Headers.ContentType.CharSet?.Replace("\"", string.Empty); // Needed since the SSRS report defaults the charset to \"utf-8\", instead of utf-8 and the code fails to find the correct encoder
var compressedContent = part.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
if (!part.Headers.ContentType.MediaType.Contains("image"))
{
var encoding = Encoding.GetEncoding(part.Headers.ContentType.CharSet);
var partContent = encoding.GetString(Convert.FromBase64String(compressedContent));
htmlView = AlternateView.CreateAlternateViewFromString(partContent, encoding, part.Headers.ContentType.MediaType);
}
else
{
linkedResources.Add(new LinkedResource(new MemoryStream(Convert.FromBase64String(compressedContent)), part.Headers.ContentType.MediaType)
{
ContentId = part.Headers.ContentDisposition.FileName.Replace("\"", string.Empty)
});
}
}
}
}
foreach (var linkedResource in linkedResources)
{
htmlView?.LinkedResources.Add(linkedResource);
}
using (var message = new MailMessage("from#mail.com", "to#mail.com", "Subject", string.Empty))
{
message.BodyEncoding = Encoding.UTF8;
message.AlternateViews.Add(htmlView);
using (var client = new SmtpClient("smtpserver.com", 25))
{
client.Send(message);
}
}

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

Posted image on a wall is too small using graph api and C#

I am trying to post a picture on a page wall with this code:
string message = "Some message";
string picture = "http://somesource.com/test.jpg";
string pageToken = "page access toke";
string pageId = "page id";
string PostUri = "https://graph.facebook.com/{0}/feed?access_token={1}&";
var request = string.Format(PostUri, pageId, pageToken);
var sb = new StringBuilder();
sb.AppendFormat("picture={0}&", picture);
sb.AppendFormat("message={0}", message);
WebClient webClient = new WebClient();
webClient.Encoding = Encoding.UTF8;
webClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
var bytes = Encoding.UTF8.GetBytes(sb.ToString());
webClient.UploadData(request, bytes);
It is works for me. But posted image using graph api has smaller size than the image posted using regular facebook web interface. What should I change in this code to display image with larger size?
Change it to source from picture
sb.AppendFormat("source={0}&", picture);
picture is used to thumbnails.
UPDATE
You can also set height and width of the image
sb.AppendFormat("height={0}&", 450);
sb.AppendFormat("width={0}&", 450);

C# Email embedded image does not display in IE

I am using C#.Net to send out a HTML email which contains embedded images. These emails work fine in Firefox and chrome but the images do not show up in Internet Explorer (IE). I know that the IE settings are not causing the problem since embedded images sent using Blat work just fine. Am I missing some option such as character set which is causing this problem? My code is as follows
MailMessage msg = new MailMessage();
MailAddress from = new MailAddress("Myemail#MyDomain", "My Name");
msg.To.Add("Myemail#MyDomain");
msg.From = from;
msg.Subject = "My subjecct line";
AlternateView htmlView = AlternateView.CreateAlternateViewFromString("This is a sample JPG embedded image<br><img src=\"cid:image1.jpg\">", null, "text/html");
LinkedResource EmbeddedObjects1 = new LinkedResource("PathToImage\\image1.jpg");
EmbeddedObjects1.ContentId = "image1.jpg";
htmlView.LinkedResources.Add(EmbeddedObjects1);
msg.AlternateViews.Add(htmlView);
SmtpClient smtpclient = new SmtpClient("mailhost.domain.com", PortNumber);
smtpclient.Send(msg);
Maybe it helps if you create the LinkedResource with a ContentType combined with Coding Gorilla's idea of a guid as the content-id:
Guid contentId = Guid.NewGuid().ToString();
AlternateView htmlView = AlternateView.CreateAlternateViewFromString(
"This is a sample JPG embedded image<br><img src=\"cid:" + contentId + "\">",
null, "text/html");
ContentType ct = new ContentType(MediaTypeNames.Image.Jpeg);
LinkedResource EmbeddedObjects1 = new LinkedResource("PathToImage\\image1.jpg", ct);
EmbeddedObjects1.ContentId = contentId;
htmlView.LinkedResources.Add(EmbeddedObjects1);
Try adding the following.
msg.IsBodyHtml = true;
Additionally, I usually setup my AlternateView like this.
AlternateView htmlView = AlternateView.CreateAlternateViewFromString(html, new ContentType("text/html"));
Embedded images work by creating dataUri schemes. IE < 8 doesn't support those.

Categories

Resources