Hi We are trying to develop a mail sending system using MailKit. We have a set of Email Templates which are created using WORD and saved as MHTML files. The whole thing is working fine, when we use MailKit to create a MimeMessage from the MHT file.
But post creating this message, I am not able to see a way of adding attachments to this.
Currently we are trying the following.
private void SendEmail(string templatePath, List<string> attachments)
{
// Load the MHT Template
var mimeMessage = MimeMessage.Load(templatePath);
mimeMessage.From.Add(new MailBoxAddress("test#Test.com"));
mimeMessage.To.Add(new MailBoxAddress("test#Test.com"));
foreach (var attachment in attachments)
{
var fileAttachment = new MimePart()
{
ContentObject = new ContentObject(File.OpenRead(Path.Combine(attachment), ContentEncoding.Default),
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
ContentTransferEncoding = ContentEncoding.Binary,
FileName = Path.GetFileName(attachment)
};
// Attachments is a read only Enumerable here.
mimeMessage.Attachments.Add
}
}
I add the attachment by the BodyBuilder:
BodyBuilder _body = new BodyBuilder
{
HtmlBody = message
};
_body.Attachments.Add(_fileName, _stream);
_email.Body = _body.ToMessageBody();
See this post stackoverflow
You will need to traverse the MIME tree structure of the message until you find the Multipart that you would like to add the "attachment" to and then use the Multipart.Add() method.
Keep in mind that a message is a nested tree structure and not a well-defined structure which has only 1 message body (or even just 2) and a list of attachments. It's a whole lot more complicated than that, so there's literally no way for MimeMessage.Attachments to "do the right thing".
For the general case, you can probably get away with something like this:
var message = MimeMessage.Load(fileName);
var attachment = new MimePart("application", "octet-stream") {
FileName = attachmentName,
ContentTransferEncoding = ContentEncoding.Base64,
Content = new MimeContent(attachmentStream)
};
if (!(message.Body is Multipart multipart &&
multipart.ContentType.Matches("multipart", "mixed"))) {
// The top-level MIME part is not a multipart/mixed.
//
// Attachments are typically added to a multipart/mixed
// container which tends to be the top-level MIME part
// of the message (unless it is signed or encrypted).
//
// If the message is signed or encrypted, though, we do
// do not want to mess with the structure, so the correct
// thing to do there is to encapsulate the top-level part
// in a multipart/mixed just like we are going to do anyway.
multipart = new Multipart("mixed");
// Replace the message body with the multipart/mixed and
// add the old message body to it.
multipart.Add(message.Body);
message.Body = multipart;
}
// Add the attachment.
multipart.Add(attachment);
// Save the message back out to disk.
message.WriteTo(newFileName);
Related
How does one go about sending a supplemental document along with the document to sign?
the use case:
We are sending a contract for the user to sign, and would like to send the person som startup/welcome information aswell.
What ive tried:
Creating the attachment
Attachment attachment = new Attachment()
{
data = Convert.ToBase64String(docStorageDto.DocumentBlob),
name = ConversionUtil.MakeLegalFileName(docStorageDto.OrigFilename),
attachmentId = displayIndex.ToString() + index,
attachmentType= "pdf"
};
Tried to add it to the envelope
envelopeDefinition.attachments = new List<Attachment>(){ attachment };
envelopeDefinition.envelopeAttachments = new List<Attachment>(){ attachment };
Tried to add it to the "Signer" object
RecipientAttachment att = new RecipientAttachment()
{
data = attachment.data,
name = attachment.name,
attachmentId = attachment.attachmentId,
attachmentType = attachment.attachmentType
};
recipientSigner.recipientAttachments = new List<RecipientAttachment>(){att};
All with no luck, fields does not seem to be used? Where does one need to add the attachments/supplemental documents?
For future reference, Larry K is right.
supplemental documents does not use the attachment properties, but is just sent as a regular document. What i needed to set for it not to be included in the combined document etc. was to set these properties on the "Document" object:
includeInDownload = "false";
display = "modal";
A supplemental document does not use an attachment object.
To add a supplemental document to an envelope:
Read the docs.
Create a document object, same as for any other document.
Set the signerMustAcknowledge attribute of the document object to one of:
no_interaction No recipient action is required.
view The recipient is required to view the document.
accept The recipient is required to accept the document by selecting accept during signing, but is not required to view the document.
no_interaction No recipient action is required.
I've been going thumbing through the documentation and searching the internet to find documenation on how to add attachments to created templates. I'm using darrencauthon's CSharp-Sparkpost to handle the API calls. So far what I have is not working. Does anyone have a working solution (possible?) or a better solution for C#? I'm not opposed to using a different library. This is the link to CSharp-Sparkpost
Here's what I've got so far:
var t = new Transmission();
t.Content.From.Email = "from#thisperson.com";
t.Content.TemplateId = "my-template-email";
new Recipient
{
Address = new Address { Email = recipient }
}
.Apply(t.Recipients.Add);
new Attachment
{
Data = //CSVDATA,
Name = "Table.csv",
Type = "text/csv"
}.Apply(t.Content.Attachments.Add);
var client = new SparkPost.Client(Util.GetPassword("sparkpostapikey"));
client.Transmissions.Send(t).Wait();
I've verified that I can send this attachment without a template and also verified that I can send this template without the attachment. So... the Email is getting sent; however, the content received is only the template and substitution data. No attachment with the template email.
Using Darren's library, and combining the requirements for my project, this is the solution I've come up with. I'm just making an additional API call to grab the template Html so I can build the transmission without having to send the template_id. Still using the CSharp-Sparkpost library to make all of the calls. I modified Darren's example SendInline program as such:
static async Task ExecuteEmailer()
{
var settings = ConfigurationManager.AppSettings;
var fromAddr = settings["fromaddr"];
var toAddr = settings["toaddr"];
var trans = new Transmission();
var to = new Recipient
{
Address = new Address
{
Email = toAddr
},
SubstitutionData = new Dictionary<string, object>
{
{"firstName", "Stranger"}
}
};
trans.Recipients.Add(to);
trans.SubstitutionData["firstName"] = "Sir or Madam";
trans.Content.From.Email = fromAddr;
trans.Content.Subject = "SparkPost sending attachment using template";
trans.Content.Text = "Greetings {{firstName or 'recipient'}}\nHello from C# land.";
//Add attachments to transmission object
trans.Content.Attachments.Add(new Attachment()
{
Data = Convert.ToBase64String(System.IO.File.ReadAllBytes(#"C:\PathToFile\ExcelFile.xlsx")),
Name = "ExcelFile.xlsx",
Type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
});
Console.Write("Sending mail...");
var client = new Client(settings["apikey"]);
client.CustomSettings.SendingMode = SendingModes.Sync;
//retrieve template html and set Content.Html
var templateResponse = await client.Templates.Retrieve("template-email-test");
trans.Content.Html = templateResponse.TemplateContent.Html;
//Send transmission
var response = client.Transmissions.Send(trans);
Console.WriteLine("done");
}
Oh actually, I see now -- you are talking about adding attachments to templates, not attachments.
My answer to that is that back when I developed this library, attachment on templates was not supported by SparkPost itself.
My library allows you to try it, but that's because every template and non-template emails are considered "transmissions." So if you create a transmission, it has the option of adding attachments... but if you send the transmission with a template id, the attachment is ignored.
I could throw an error, or somehow design the API around this limitation, but what if they stopped ignoring the attachment but my library threw an error? I designed the library for flexibility as the SparkPost web API grew, and I didn't want my library to get in the way.
If you want to test if you're sending the attachment right, send your transmission without a transmission id, and instead with a subject and email body. If the email goes through and you get an attachment, then you know it's because of this template/attachment restriction from SparkPost.
NOTE: I'm putting this answer on Stack Overflow, and it's possible that this dated message will no longer be valid in the future.
I'm Darren Cauthon, the primary author of this library.
I have attachment support in my acceptance tests, which are run before each release. The link is below, but the code should be as simple as:
// C#
var attachment = File.Create<Attachment>("testtextfile.txt");
transmission.Content.Attachments.Add(attachment);
https://github.com/darrencauthon/csharp-sparkpost/blob/3a8cb1efbb8c9a0448c71c126ce7f88759867fb0/src/SparkPost.Acceptance/TransmissionSteps.cs#L56
As the title, is MailKit supported to send file?
If yes, how can I do it?
Yes. This is explained in the documentation as well as the FAQ.
From the FAQ:
How do I create a message with attachments?
To construct a message with attachments, the first thing you'll need to do is create a multipart/mixed container which you'll then want to add the message body to first. Once you've added the body, you can then add MIME parts to it that contain the content of the files you'd like to attach, being sure to set the Content-Disposition header value to the attachment. You'll probably also want to set the filename parameter on the Content-Disposition header as well as the name parameter on the Content-Type header. The most convenient way to do this is to simply use the MimePart.FileName property which
will set both parameters for you as well as setting the Content-Disposition header value to attachment if it has not already been set to something else.
var message = new MimeMessage ();
message.From.Add (new MailboxAddress ("Joey", "joey#friends.com"));
message.To.Add (new MailboxAddress ("Alice", "alice#wonderland.com"));
message.Subject = "How you doin?";
// create our message text, just like before (except don't set it as the message.Body)
var body = new TextPart ("plain") {
Text = #"Hey Alice,
What are you up to this weekend? Monica is throwing one of her parties on
Saturday. I was hoping you could make it.
Will you be my +1?
-- Joey
"
};
// create an image attachment for the file located at path
var attachment = new MimePart ("image", "gif") {
Content = new MimeContent (File.OpenRead (path)),
ContentDisposition = new ContentDisposition (ContentDisposition.Attachment),
ContentTransferEncoding = ContentEncoding.Base64,
FileName = Path.GetFileName (path)
};
// now create the multipart/mixed container to hold the message text and the
// image attachment
var multipart = new Multipart ("mixed");
multipart.Add (body);
multipart.Add (attachment);
// now set the multipart/mixed as the message body
message.Body = multipart;
A simpler way to construct messages with attachments is to take advantage of the
BodyBuilder class.
var message = new MimeMessage ();
message.From.Add (new MailboxAddress ("Joey", "joey#friends.com"));
message.To.Add (new MailboxAddress ("Alice", "alice#wonderland.com"));
message.Subject = "How you doin?";
var builder = new BodyBuilder ();
// Set the plain-text version of the message text
builder.TextBody = #"Hey Alice,
What are you up to this weekend? Monica is throwing one of her parties on
Saturday. I was hoping you could make it.
Will you be my +1?
-- Joey
";
// We may also want to attach a calendar event for Monica's party...
builder.Attachments.Add (#"C:\Users\Joey\Documents\party.ics");
// Now we just need to set the message body and we're done
message.Body = builder.ToMessageBody ();
For more information, see Creating Messages.
#jstedfast brought pretty cool solution, here are a few more examples of simple ways to just send a file as an attachment (pdf document in this case, but can be applied to any file type).
var message = new MimeMessage();
// add from, to, subject and other needed properties to your message
var builder = new BodyBuilder();
builder.HtmlBody = htmlContent;
builder.TextBody = textContent;
// you can either create MimeEntity object(s)
// this might get handy in case you want to pass multiple attachments from somewhere else
byte[] myFileAsByteArray = LoadMyFileAsByteArray();
var attachments = new List<MimeEntity>
{
// from file
MimeEntity.Load("myFile.pdf"),
// file from stream
MimeEntity.Load(new MemoryStream(myFileAsByteArray)),
// from stream with a content type defined
MimeEntity.Load(new ContentType("application", "pdf"), new MemoryStream(myFileAsByteArray))
}
// or add file directly - there are a few more overloads to this
builder.Attachments.Add("myFile.pdf");
builder.Attachments.Add("myFile.pdf", myFileAsByteArray);
builder.Attachments.Add("myFile.pdf", myFileAsByteArray , new ContentType("application", "pdf"));
// append previously created attachments
foreach (var attachment in attachments)
{
builder.Attachments.Add(attachment);
}
message.Body = builder.ToMessageBody();
Hope it helps.
You can also send multiple files using this approach directly.
**Note: files used here is IEnumerable files **
try
{
var message = new MimeMessage();
message.From.Add(new MailboxAddress(emailService.FromFullName, emailService.FromEmail));
message.To.AddRange(emailsToSend.Select(x => new MailboxAddress(x)));
message.Subject = subject;
var builder = new BodyBuilder();
builder.HtmlBody = body;
foreach (var attachment in files)
{
if (attachment.Length > 0)
{
string fileName = Path.GetFileName(attachment.FileName);
builder.Attachments.Add(fileName, attachment.OpenReadStream());
}
}
message.Body = builder.ToMessageBody();
}
I'm trying to generate a MailMessage and set various attachments to it. The inline attachments always appear to the recipient as either jpeg, png, or other image files if I add them as an Attachment type. The code I used for this approach:
var mailMessage = new MailMessage();
// Set To, From, Body, Subject, etc.
foreach(var att in self.Attachments) {
byte[] content = att.GetBytes();
var attachment = new Attachment(new MemoryStream(content), att.Name);
if(att.IsInline){
attachment.ContentId = att.Name;
attachment.ContentDisposition.Inline = true;
attachment.ContentDisposition.DispositionType = DispositionTypeNames.Inline;
}
mailMessage.Attachments.Add(attachment)
}
var view = AlternateView.CreateAlternateViewFromString(mailMessage.Body, Encoding.UTF8, MediaTypeNames.Text.Html);
mailMessage.AlternateViews.Add(view);
If I add them as a LinkedResource then they show up as dat files in the attachment section. Code:
var mailMessage = new MailMessage();
// Set To, From, Body, Subject, etc.
var view = AlternateView.CreateAlternateViewFromString(mailMessage.Body, Encoding.UTF8, MediaTypeNames.Text.Html);
foreach(var att in self.Attachments) {
byte[] content = att.GetBytes();
if(att.IsInline) {
var inline = new LinkedResource(new MemoryStream(content), att.ContentType);
inline.ContentId = att.Name;
view.LinkedResources.Add(inline);
}
else {
mailMessage.Attachments.Add(new Attachment(new MemoryStream(content),
att.Name));
}
}
mailMessage.AlternateViews.Add(view);
Both approaches generate the correct email and inject the inline attachments into the body of the email. Neither show the inline attachments in the attachment section while previewing the email in outlook. Both show the inline attachments when receiving the email in outlook. I have made sure that the outlook settings are as follows in the mail section of Outlook Options: "Compose message in this format: HTML" and "When sending messages in Rich Text format to Internet recipients: Convert to HTML format".
Any suggestions would be greatly appreciated as the additional attachments are creating confusion to the end users.
I stumbled on this question having had the same problem, and for me the solution was to add the type of the attachment.
Change this
var inline = new LinkedResource("logo.png");
To this
var inline = new LinkedResource("logo.png", "image/png");
It looks as if you are already doing this, but perhaps that wasn't working as intended?
Thanks to this page for providing the answer:
https://www.codeproject.com/articles/31897/embed-an-image-in-email-using-asp-net
I only need header and metadata of attachments from Gmail, I don't want to get email body or the content of the attachments, because they may be very big and I don't need them.
Is there a way to do so through AE.Net.Mail or gmail api? It seems only come with one option of of headers only, which will not fetch the attachment metadata for me.
MailMessage[] GetMessages(string startUID, string endUID, bool headersonly = true, bool setseen = false);
Eventually, I gave up on AE.Net.Mail and switched to Gmail API instead. And I am able to get attachment meta data from the Message Get Request, without getting the actual attachment file.
https://developers.google.com/gmail/api/v1/reference/users/messages/get
If you use MailKit instead of AE.Net.Mail, you can get the metadata of attachments using the following code snippet:
using (var client = new ImapClient ()) {
client.Connect ("imap.gmail.com", 993, true);
client.Authenticate ("username", "password");
client.Inbox.Open (FolderAccess.ReadWrite);
var summaries = client.Fetch (0, -1, MessageSummaryItems.UniqueId |
MessageSummaryItems.BodyStructure | // metadata for mime parts
MessageSummaryItems.Envelope); // metadata for messages
foreach (var summary in summaries) {
// Each summary item will have the UniqueId, Body and Envelope properties
// filled in (since that's what we asked for).
var uid = summary.UniqueId.Value;
Console.WriteLine ("The UID of the message is: {0}", uid);
Console.WriteLine ("The Message-Id is: {0}", summary.Envelope.MessageId);
Console.WriteLine ("The Subject is: {0}", summary.Envelope.Subject);
// Note: there are many more properties, but you get the idea...
// Since you want to know the metadata for each attachment, you can
// now walk the MIME structure via the Body property and get
// whatever details you want to get about each MIME part.
var multipart = summary.Body as BodyPartMultipart;
if (multipart != null) {
foreach (var part in multipart.BodyParts) {
var basic = part as BodyPartBasic;
if (basic != null && basic.IsAttachment) {
// See http://www.mimekit.net/docs/html/Properties_T_MailKit_BodyPartBasic.htm
// for more details on what properties are available.
Console.WriteLine ("The size of this attachment is: {0} bytes", basic.Octets);
Console.WriteLine ("The file name is: {0}", basic.FileName);
// If you want to download just this attachment, you can download it like this:
var attachment = client.Inbox.GetBodyPart (uid, basic);
}
}
}
}
client.Disconnect (true);
}
Keep in mind that since MIME is a tree structure and not a flat list, you'll realistically want to walk multiparts recursively.