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?
Related
I'm using updating out Exchange Mail Client written in .NET 4 Framework using Exchange Web Services (EWS) to .NET Core 6 using Microsoft.Graph. I've managed to port most of the functions but I'm having trouble working out how to Forward a Message as an Attachment.
In EWS
// This method results in a GetItem call to EWS.
var msgToAttach = EWS.EmailMessage.Bind(
ServiceInstance,
message.Source.Id,
new EWS.PropertySet(EWS.ItemSchema.MimeContent, EWS.ItemSchema.Subject));
// Create an email message and set properties on the message.
var forward = new EWS.EmailMessage(ServiceInstance);
if (!bodyType.HasValue)
bodyType = message.BodyType;
forward.Subject = subject == null ? $"FW: {message.Subject}" : subject;
forward.Body = new EWS.MessageBody(
bodyType.Value == BodyTypes.Html ? EWS.BodyType.HTML : EWS.BodyType.Text,
body ?? "Please refer to the attachment message");
// Add additional recipients to the reply email message.
if (to?.Any() ?? false)
forward.ToRecipients.AddRange(to);
if (cc?.Any() ?? false)
forward.CcRecipients.AddRange(cc);
if (bcc?.Any() ?? false)
forward.BccRecipients.AddRange(bcc);
// before we can add the attachments, we need to save the draft
// according to the docoumentation this isn't required but without
// it throws an exception on Save/SendAndSaveCopy if the attachments
// are added to a message which doesn't exist
forward.Save();
// Add an email message item attachment and set properties on the item.
EWS.ItemAttachment<EWS.EmailMessage> itemAttachment = forward.Attachments.AddItemAttachment<EWS.EmailMessage>();
itemAttachment.Item.MimeContent = msgToAttach.MimeContent;
itemAttachment.Name = msgToAttach.Subject;
// Send the mail and save a copy in the Sent Items folder.
// This method results in a CreateItem and SendItem call to EWS.
forward.Update(EWS.ConflictResolutionMode.AlwaysOverwrite);
forward.Send();
UPDATE: Solution
You can read the mime content using
_serviceInstance.Users[UserId].Messages[message.Id].Content.GetAsync()
which returns a stream that can be saved as .eml file. The attachment name must end with .eml or it doesn't work.
public void ForwardMessageAsAttachment(
Message message,
IEnumerable<string> to,
string subject = null,
string body = null,
BodyType? bodyType = null)
{
// Download the mime content for the specific message
var mimeContentStream = _serviceInstance
.Users[UserId]
.Messages[message.Id]
.Content
.Request()
.GetAsync()
.GetAwaiter()
.GetResult();
// the mine content is returned as a Stream, so write it to buffer
var buffer = new Span<Byte>(new byte[mimeContentStream.Length]);
var mimeContent = mimeContentStream.Read(buffer);
// The attachment Name must ends with .eml else Outlook wont recognize its an email
// even with the contentType = message/rfc822
var messageAsAttachment = new FileAttachment
{
ContentBytes = buffer.ToArray(),
ContentType = "message/rfc822",
Name = $"{message.Subject}.eml"
};
// create a new message
var forward = new Message()
{
Attachments = new MessageAttachmentsCollectionPage(),
Subject = String.IsNullOrWhiteSpace(subject) ? subject : $"FW: {message.Subject}".Trim(),
Body = new ItemBody
{
ContentType = bodyType,
Content = body
},
ToRecipients = to.Select(x => new Recipient { EmailAddress = new EmailAddress { Address = x } })
};
// add the EML attachment
forward.Attachments.Add(messageAsAttachment);
_serviceInstance
.Users[UserId]
.SendMail(forward)
.Request()
.PostAsync()
.GetAwaiter()
.GetResult();
}
In the Graph there is no equivalent to
EWS.ItemAttachment<EWS.EmailMessage> itemAttachment =
forward.Attachments.AddItemAttachment<EWS.EmailMessage>();
itemAttachment.Item.MimeContent = msgToAttach.MimeContent;
So your options are to attach the message as an EML file as a File Attachment
or you can build the MIME message outside with a MIME parser like mailkit and then send the message as MIME https://github.com/microsoftgraph/microsoft-graph-docs/blob/main/concepts/outlook-send-mime-message.md you will be limited on size with this method you can have anything larger then 4MB and with mimebloat that usually means around 2-3 MB
Mime content from a message can be read using MS Graph and converted to .EML, then sent FileAttachment on a new message. You need to make sure the attachment's name ends with .eml or outlook will not recognize attachment as a message. Adding content type of message/rfc822 does not appear to affect how the attachment is read by gmail, hotmail or Outlook desktop.
public void ForwardMessageAsAttachment(
Message message,
IEnumerable<string> to,
string subject = null,
string body = null,
BodyType? bodyType = null)
{
// Download the mime content for the specific message
// _serviceInstance is an instance of GraphServiceClient
var mimeContentStream = _serviceInstance
.Users[UserId]
.Messages[message.Id]
.Content
.Request()
.GetAsync()
.GetAwaiter()
.GetResult();
// the mine content is returned as a Stream, so write it to buffer
var buffer = new Span<Byte>(new byte[mimeContentStream.Length]);
var mimeContent = mimeContentStream.Read(buffer);
// The attachment Name must ends with .eml else Outlook wont recognize its an email
// even with the contentType = message/rfc822
var messageAsAttachment = new FileAttachment
{
ContentBytes = buffer.ToArray(),
ContentType = "message/rfc822",
Name = $"{message.Subject}.eml"
};
// create a new message
var forward = new Message()
{
Attachments = new MessageAttachmentsCollectionPage(),
Subject = String.IsNullOrWhiteSpace(subject) ? subject : $"FW: {message.Subject}".Trim(),
Body = new ItemBody
{
ContentType = bodyType,
Content = body
},
ToRecipients = to.Select(x => new Recipient { EmailAddress = new EmailAddress { Address = x } })
};
// add the EML attachment
forward.Attachments.Add(messageAsAttachment);
_serviceInstance
.Users[UserId]
.SendMail(forward)
.Request()
.PostAsync()
.GetAwaiter()
.GetResult();
}
I have an API which gives me path to a file http://bts.myurl.com/ThisIsMyPdf.pdf.
Now I have a button which onto clicking share this file to users via mail. This is the code which i have used for sending the report:
var filePath = "http://bts.myurl.com/ThisIsMyPdf.pdf";
Utilities.SendEmail("MyId#gmail.com", "subject", "To#gmail.com", "", "", "body", filePath);
But this is giving exception as URI Formats are not supported.
Some other approaches include the file to be download first before sending as attachment but again i don't want it to be downloaded.
I believe there are other ways to achieve this, if any please share.
As suggested by #dlatikay, hereby sharing a working code for the above problem.
//This is the code get byte stream from the URL
WebClient myClient = new WebClient();
byte[] bytes = myClient.DownloadData("http://www.examle.com/mypdf.pdf");
System.IO.MemoryStream webPdf = new MemoryStream(bytes);
//To Create the Attachment for sending mail.
System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Application.Pdf);
Attachment attach = new Attachment(webPdf, ct);
attach.ContentDisposition.FileName = "myFile.pdf";
var smtp = new SmtpClient
{
Host = "email.domainName.com",
Port = 587,
EnableSsl = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(fromAddress.Address, fromPassword)
};
using (var message = new MailMessage(fromAddress, toAddress)
{
Subject = subject,
Body = body
})
{
//Here webpdf is the bytestream which is going to attach in the mail
message.Attachments.Add(new Attachment(webPdf, "sample.pdf"));
smtp.Send(message);
}
webPdf.Dispose();
webPdf.Close();
I encrypted an image,
Now I need to read that image, decrypt and attach it into an email.
For first step I try to put an image file and attach it into an Email,
but when i receive email, the attached image is corrupted !
I try many different ways, but without success.
(I created windows application project just for test, Eventually I need to use solution in MVC Web Application project)
private void btnSend_Click(object sender, EventArgs e)
{
var filePath = "D:\\3.jpg"; // path to none encrypted image file
var ms = new MemoryStream(File.ReadAllBytes(filePath));
// Create attachment
var attach = new Attachment(ms, new ContentType(MediaTypeNames.Image.Jpeg));
attach.ContentDisposition.FileName = "sample.jpg";
// Send Email
IMailSender mailSender = new MailSender();
var isSuccess = mailSender.Send(
"sample email title",
"sample#gmail.com",
"sample subject",
"sample body",
new Attachment[] { attach });
MessageBox.Show(isSuccess ? "Email sent successfully" : mailSender.ErrorMessage);
}
using (MailMessage Message = new MailMessage())
{
Message.From = new MailAddress("from#mail.com");
Message.Subject = "My Subject";
Message.Body = "My Body";
Message.To.Add(new MailAddress("to#mail.com"));
//Attach more file
foreach (var item in Attachment)
{
MemoryStream ms = new MemoryStream(File.ReadAllBytes(filePath));
Attachment Data = new Attachment(ms, "FileName");
ContentDisposition Disposition = Data.ContentDisposition;
Disposition.CreationDate = DateTime.UtcNow.AddHours(-5);
Disposition.ModificationDate = DateTime.UtcNow.AddHours(-5);
Disposition.ReadDate = DateTime.UtcNow.AddHours(-5);
Data.ContentType = new ContentType(MediaTypeNames.Application.Pdf);
Message.Attachments.Add(Data);
}
SmtpClient smtp = new SmtpClient("SmtpAddress", "SmtpPort");
smtp.Credentials = new NetworkCredential("SmtpUser", "SmtpPassword");
await smtp.SendMailAsync(Message);
}
I hope this helps
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.
Hi I am trying to send bulkmail with attachment using amazon ses. I can able to send mails with attachment but my to-mails are appearing for all the users that I have send. I am trying to add those destination mails in bcc fields but it is throwing an error Empty required header 'To'.
This is what I've already tried:
private static BodyBuilder GetMessageBody()
{
var body = new BodyBuilder()
{
HtmlBody = #"<p>Amazon SES Test body</p>",
TextBody = "Amazon SES Test body",
};
body.Attachments.Add(#"G:\me.jpg");
return body;
}
private static MimeMessage GetMessage()
{
var message = new MimeMessage();
List<string> to = new List<string>(50);
to.Add("xxxxxx#gmail.com");
to.Add("xxxxxx#gmail.com");
message.From.Add(new MailboxAddress(ConfigurationManager.AppSettings["senderaddress"], ConfigurationManager.AppSettings["senderaddress"]));
for (int i = 0; i < to.Count; i++)
{
message.Bcc.Add(new MailboxAddress(string.Empty,to[i]));
//message.To.Add(new MailboxAddress(string.Empty, "xxxxx#gmail.com"));
//message.To.Add(new MailboxAddress(string.Empty, "xxxxxx#gmail.com"));
}
message.Subject = "Amazon SES Test";
message.Body = GetMessageBody().ToMessageBody();
return message;
}
private static MemoryStream GetMessageStream()
{
var stream = new MemoryStream();
GetMessage().WriteTo(stream);
return stream;
}
private void SendEmails()
{
var credentals = new BasicAWSCredentials(ConfigurationManager.AppSettings["AccessKey"], ConfigurationManager.AppSettings["SecretAccessKey"]);
using (var client = new AmazonSimpleEmailServiceClient(credentals, RegionEndpoint.USEast1))
{
var sendRequest = new SendRawEmailRequest { RawMessage = new RawMessage(GetMessageStream()) };
try
{
var response = client.SendRawEmail(sendRequest);
}
catch (Exception e)
{
}
}
}
You need at least 1 email address in to 'To' field. Perhaps send the email to yourself and add the others as a BCC.
Instead of using the BCC header to send out bulk mails, IMHO you should send one email with a clear "to" header to each of the recipients. So instead looping through recipients and adding them to BCC you should rather create and send a message there.