What I want to achieve:
Scan mails and attach relevant ones to a "summary"-mail.
My problem:
I can't seem to find any information about how this is done. When using for example Outlook you can simply drag and drop a mail into another message thus attaching it. I looked through the headers and found that it's basically the mail's content and attachments with their content types attached without further encoding. But attaching this data to a MailMessage via Attachment.CreateAttachmentFromString didn't work out either, the file was displayed as a regular file.
My current code:
var mail = new MailMessage(settings.Username, to);
var smtp = new SmtpClient(settings.SMTP, settings.Port);
// ...
// authentication + packing stuff into subject and body
// ...
foreach (var att in attachments)
{
Attachment attachment = Attachment.CreateAttachmentFromString(att.Text, att.Filename);
mail.Attachments.add(attachment);
}
client.Send(mail);
client.Dispose();
mail.Dispose();
My question:
Can C# do this out of the box using some hack or are there libraries that support that?
You would probably want to just use the Attachment constructor that takes a file name:
Attachment attachment = new Attachment(att.Filename);
mail.Attachments.add(attachment);
Of course, this assumes you've saved the attachment already out to your file system somewhere.
You could also just use the attachment's content stream to avoid the overhead of saving each attachment to file first:
Attachment attachment = new Attachment(att.ContentStream, String.Empty);
mail.Attachments.add(attachment);
NOTE: the second argument to that constructor is the "content type", which, if left as an empty string, will be text/plain; charset=us-ascii. Refer to RFC 2045 Section 5.1 for more content types.
Also, see MSDN for more Attachment constructor overloads: https://msdn.microsoft.com/en-us/library/System.Net.Mail.Attachment.Attachment%28v=vs.110%29.aspx
Well, I found a way to somehow does what I needed. This solution is not the perfect answer, but it works almost as intended.
Warning
This solution requires currently Outlook installed as the mail needs to be attached as a .msg file. I want to repeat that this is not the right way to go, this method is way slower than any other solution but it works. I will further investigate soon.
But for now, here's my Extension class:
using System;
using System.Net.Mail;
using System.IO;
using Outlook = Microsoft.Office.Interop.Outlook;
namespace MailAttachment
{
public static class Extensions
{
public static string AttachMail(this MailMessage mail, MailMessage otherMail)
{
string path = Path.GetTempPath(),
tempFilename = Path.Combine(path, Path.GetTempFileName());
Outlook.Application outlook = new Outlook.Application();
Outlook.MailItem outlookMessage;
outlookMessage = outlook.CreateItem(Outlook.OlItemType.olMailItem);
foreach (var recv in message.To)
{
outlookMessage.Recipients.Add(recv.Address);
}
outlookMessage.Subject = mail.Subject;
if (message.IsBodyHtml)
{
outlookMessage.BodyFormat = Outlook.OlBodyFormat.olFormatHTML;
outlookMessage.HTMLBody = message.Body;
}
else
{
outlookMessage.Body = message.Body;
}
outlookMessage.SaveAs(tempFilename);
outlookMessage = null;
outlook = null;
Attachment attachment = new Attachment(tempFilename);
attachment.Name = mail.Subject + ".msg";
otherMail.Attachments.Add(attachment);
return tempFilename;
}
}
}
Additional information
This solution requires you to delete the temporary file after you sent the mail. This might look like this:
MailMessage mail = new MailMessage();
List<MailMessage> mailsToAttach = mails.FindAll(m => m.Date.CompareTo(otherDate) < 0);
List<string> tempFiles = new List<string>();
foreach (var item in mailsToAttach)
{
string tempFile = mail.AttachMail(item);
tempFiles.Add(tempFile);
}
// smtp.Send(mail)
foreach (var item in tempFiles)
{
System.IO.File.Delete(item);
}
Related
I want to send a (view page) pdf sending by email.but when I trying to add an attachment, I found an error below "Add". for that I can't successfully sending mail with attaching my view pdf.
Here is my code:
(Ordercontroller)
//other code
var message = new MimeMessage();
message.From.Add(new MailboxAddress("Test Project", "pt300#gmail.com"));
message.To.Add(new MailboxAddress("psm", "p689#gmail.com"));
message.Subject = "Hi,this is demo email";
message.Body = new TextPart("plain")
{
Text = "Hello,My First Demo Mail it is.Thanks",
};
//add attach
var aa = new ViewAsPdf("Cart")
{
FileName = "Invoice.pdf", //actually, I don't know why this filename is
// "Invoice". I found this system on a website.
PageOrientation = Rotativa.AspNetCore.Options.Orientation.Portrait,
};
message.Attachments.Add(aa);
//end attach
using (var client = new SmtpClient())
{
client.Connect("smtp.gmail.com", 587, false);
client.Authenticate("pt300#gmail.com", "MyPassword");
client.Send(message);
client.Disconnect(true);
}
//other code
and these controller's view I trying to pdf and send by mail:
(HomeController)
public ActionResult Cart()
{
List<Product> products = HttpContext.Session.Get<List<Product>>("products");
if (products == null)
{
products = new List<Product>();
}
return View(products);
}
in OrderController I found an error.
how I will solve this problem and successfully send my view's pdf by mail.please help.
Attachments is a MimeMessage property that has no such method Add, so here comes the error. To add the attachments you should create a BodyBuilder object before, with this class you will be able to add new attachments (each one of them as byte array), usign Attachments Property and its Add method, but always related to BodyBuilder, not to MimeMessage.
Please, take a look to the answer given here, as I guess is what you are looking for:
.net Core Mailkit send attachement from array
Or check another example here:
https://www.taithienbo.com/send-email-with-attachments-using-mailkit-for-net-core/
In addition, you could get Rotativa PDF as byte array usign this code:
var rotativaAction = new ViewAsPdf("Cart")
{
FileName = "Invoice.pdf", // You can change this file name to set whatever you want
PageOrientation = Rotativa.AspNetCore.Options.Orientation.Portrait,
};
byte[] fileAttachmetByteArray = rotativaAction.BuildFile(ControllerContext);
Once you have generated your PDF as a byte array using Rotativa, guessing you have stored the result in a variable called fileAttachmentByteArray (as stated in the previous code sample), you must simply send this variable (the byte array) to the Add method of the BodyBuilder, as the second parameter (first is the attachement file name, which is completely free to use the name you prefer), and then set Mime Message Body, usign the BodyBuilder you have worked with.
So, to explain the process in steps:
On the first line you create a BodyBuilder variable, and initialize it, using the text you want as body for the email.
Then, you call Add method to add a new attachment, sending to it the file name you wish and your previously created bye array
Finally, you asign the value to the Body property of your Mime Message
And your code should look like this:
var builder = new BodyBuilder { HtmlBody = "yourBodyMessage" }; // Change "yourBodyMessage" to the email body you desire
builder.Attachments.Add("filename", fileAttachmentByteArray); // Once again, you can change this "filename" to set whatever you want
mimeMessage.Body = builder.ToMessageBody(); // Assuming mimeMessage is the same variable you provided in your code
This seems like a tough one to find a good answer to. I want to create a mail message, add attachments to it, encrypt it using a X509Certificate2 certificate, and then send it. Sounds simple enough, right?! I use asp.net mvc and C#.
This is what I have so far.
string sMTPClient = ConfigurationManager.AppSettings.Get("SMTPClient");
using (var smtpClient = new SmtpClient(sMTPClient))
{
var attachments = MethodToCreateMailAttachments(......);
X509Certificate2 certificate = MethodToGetCertificateBySerialNumber("xxxxxxx");
using (var finalMailmessage = new MailMessage())
{
var encryptedMailMessage = new MailMessage();
var encryptCert = new X509Certificate2(certificate);
encryptedMailMessage.Subject = mailsubject;
encryptedMailMessage.Body = mailBody;
if (attachments.Any())
{
foreach (var item in attachments)
encryptedMailMessage.Attachments.Add(item);
}
byte[] encryptedBodyBytes = Encoding.ASCII.GetBytes(encryptedMailMessage.ToString());
EnvelopedCms Envelope = new EnvelopedCms(new ContentInfo(encryptedBodyBytes));
CmsRecipient Recipient = new CmsRecipient(SubjectIdentifierType.IssuerAndSerialNumber, encryptCert);
Envelope.Encrypt(Recipient);
byte[] EncryptedBytes = Envelope.Encode();
//Attach the encrypted message as an alternate view.
MemoryStream ms = new MemoryStream(EncryptedBytes);
AlternateView av = new AlternateView(ms, "application/pkcs7-mime; smime-type=signed-data;name=smime.p7m");
finalMailmessage.AlternateViews.Add(av);
finalMailmessage.From = new MailAddress(mailFrom);
foreach (var address in mailTo.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries))
{
finalMailmessage.To.Add(address);
}
var smtp = new SmtpClient(sMTPClient);
smtp.Send(finalMailmessage);
finalMailmessage.Dispose();
ErrorLogging.log.Debug("Mailmessage sent");
return "";
}
}
What this does is create two MailMessages, one for the things that need to be encrypted, attachments, body and subject. Then I create the message that will be sent. To this I add the first message as an alternate view. This works so far as to encrypt and send the email, and on the recieving end, I get an email with a padlock icon in Outlook.
I can then open the message in Outlook, by importing the certificate. This works. However, next to the padlock icon, in Outlook, I get the attachment paperclip icon, which suggests that there is something attached to the message. But the message is empty. So nothing gets attached apparently. I suspect the adding of the encrypted MailMessage as an alternate view to the other MailMessage, is where I have gone wrong.
I've tried a lot of other things with no luck, and this is the closest I have come to a working solution. I need some input, so does anyone have any suggestions?
I found a working solution. The problem was this part:
byte[] encryptedBodyBytes = Encoding.ASCII.GetBytes(encryptedMailMessage.ToString());
I had to create a memorystream, which can then be converted into a byte array. I used a tool called "MimeKit", which can be installed as a nuget package.
So, instead I have:
var memStream = new MemoryStream();
var mimeMessage = MimeMessage.CreateFromMailMessage(encryptedMailMessage);
mimeMessage.WriteTo(memStream);
var messageString = Encoding.UTF8.GetString(memoryStream.ToArray());
byte[] encryptedBodyBytes = Encoding.ASCII.GetBytes(messageString);
The rest is the same.
I am having a client application which will save eml files to the local disk. Need to get the attachment inside the eml file which is saved without using the exchange service because the Mailbox keeps changing of its capacity.Please help if anyone have come across similar issue
I have tried the reverse process of getting the eml file and load it again to get the details.
You could use something like MimeKit for this. The GitHub page has examples on how to parse MIME messages and how to get attachments.
Here is an example of how to get the attachments in an array of bytes:
var mimeMessage = MimeMessage.Load(#"test.eml");
var attachments = mimeMessage.Attachments.ToList();
foreach (var attachment in attachments)
{
using (var memory = new MemoryStream())
{
if (attachment is MimePart)
((MimePart)attachment).Content.DecodeTo(memory);
else
((MessagePart)attachment).Message.WriteTo(memory);
var bytes = memory.ToArray();
}
}
First of all Big Thanks to MadDev for helping out !!!
Here is the Code which I used:
Note: Here in case, the stored email will always have another eml file attached to it and this is based on the business logic.
protected static void MimeProcessor(MemoryStream stream)
{
try
{
var parser = new MimeParser(stream, MimeFormat.Default);
var message = parser.ParseMessage();
var multipart = message.Body as Multipart;
//Found the Attachment as Message Part
var OriginalMessage = multipart.ToList().LastOrDefault();
if (OriginalMessage is MessagePart)
{
using (var memory = new MemoryStream())
{
((MessagePart)OriginalMessage).Message.WriteTo(memory);
var bytes = memory.ToArray();
File.WriteAllBytes("C:\\Test\\TestMessage.eml", bytes);
}
}
}
catch (Exception)
{
throw;
}
}
I need to be able to send textual data stored in a database in multiple row format as txt file attachments in an email. Think Notes added to something by different people at different times.
As of right now the email sends and there are some text files generated like test0.txt and test1.txt but they're empty. I'm flushing the streamwriter so to me it seems like the file should have text in it. I can't close the stream writer before I send the email because then I get an error saying cannot read from closed stream. I'm storing both the memorystream and streamwriter in containers so they shouldn't get disposed of or cleaned up until the very end. I wonder if the email object gets lost or for some reason can't access the streams stored in the containers?
I've looked at some similar questions on SO but they don't seem to work.
This person is using byte[] so only memory stream no streamwriter
This person disposes of their streamwriter before sending email which errors out for me
Instead of trying to do this in memory should I just be writing temp files and then including those as attachments? It seems slow to write to disk just so that I can then read from disk to attach the attachment.
var companyEmail = new MailAddress("address#company.com", "Person Name");
email.To.Add(companyEmail);
email.From = new System.Net.Mail.MailAddress("donotreply#company.com", "doesn't matter");
email.Subject = "subject";
email.Body = "body";
email.IsBodyHtml = true;
var nonAttCounter = 0;
var nonAttStreamHolder = new List<MemoryStream>();
var nonAttWriterHolder = new List<StreamWriter>();
//churn through the attachments and see if any of them are checked in the form
foreach (DataRow datarow in claim.attachments.Rows)
{
string cbFormName = "ctl00$MainBody$att" + datarow["attNum"].ToString();//name of checkbox controls on page and in form.
var includedInForm = rForm[cbFormName];
//see if the attachment was selected as one to include.ie the attNum is in the posted form.
if (includedInForm != null)
{
string origData = datarow["origData"].ToString();
string[] fIDs = origData.Split(',');
foreach (var item in fIDs)
{
//not all attachments are real attachments with files...cause why would attachments be attachments.
int fid;
bool isInt = int.TryParse(item, out fid);
if (isInt)
{
var tempDS = new datastore(ref fid);
var tempData = tempDS.blobData;
email.Attachments.Add(new Attachment(tempData, tempDS.fileNameWithExt));
}
else
{
//grab all the textual data from the database for this "attachment" and write it to a memory stream and upload to the email
nonAttStreamHolder.Add(new MemoryStream());
nonAttWriterHolder.Add(new StreamWriter(nonAttStreamHolder[nonAttCounter]));
nonAttWriterHolder[nonAttCounter].WriteLine("This is a test.");
nonAttWriterHolder[nonAttCounter].WriteLine("Why this no work?!");
nonAttWriterHolder[nonAttCounter].Flush();
//nonAttWriterHolder[nonAttCounter].Close();
System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Text.Plain);
System.Net.Mail.Attachment tempFile = new System.Net.Mail.Attachment(nonAttStreamHolder[nonAttCounter], ct);
tempFile.ContentDisposition.FileName = "test" + nonAttCounter + ".txt";
email.Attachments.Add(tempFile);
nonAttCounter++;
}
}
}
}
Global_Utilities.SharedFunctions.emailQuickSend(email);
foreach (var writer in nonAttWriterHolder)
{ writer.Close(); }
foreach (var stream in nonAttStreamHolder)
{ stream.Close(); }
You can simply take your string(s) and encode them as byte[], then use the techniques outlined in attach multiple files to an email programticaly without writing to disk. Here's some simple code to get you started:
var myString = "This is a test.";
var myBytes = System.Text.Encoding.UTF8.GetBytes(myString);
Now myBytes is an array of bytes. Note that you'll likely want to specify the encoding of the attachment, via the TransferEncoding property.
I have created an application at work that generates exel files from some database data. After generating the files they are sent automatically to the customers in question. My problem is that it works fine when i run the published application. But some users when they run the application the files are generated perfectly as they are saved on the HDD and i can see them. But when they are attached to the MailMessage object they get corrupted. This is an image of the corrupted files. These files should be Excel files.
This is my code for sending a mail with attached files:
public void SendMailedFilesDK()
{
string[] sentFiles = Directory.GetFiles(sentFilesDK);
if (sentFiles.Count() > 0)
{
using (System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient("ares"))
{
using (System.Net.Mail.MailMessage msg = new System.Net.Mail.MailMessage())
{
msg.From = new MailAddress("system#mail.dk");
msg.To.Add(new MailAddress("operation#mail.dk"));
msg.To.Add(new MailAddress("bl#mail.dk"));
msg.CC.Add("lmy#mail.dk");
msg.CC.Add("ltr#mail.dk");
msg.Subject = "IBM PUDO";
msg.Body = sentFiles.Count() + " attached file(s) has been sent to the customer(s) in question ";
msg.IsBodyHtml = true;
foreach (string file in sentFiles)
{
Attachment attachment = new Attachment(file);
msg.Attachments.Add(attachment);
}
client.Send(msg);
}
}
}
}
Why are the files getting corrupted when others run the application? We are all using office 2010.
You should make sure to set the content type of the attachement to the appropriate value.
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet for xlsx files, or
application/vnd.ms-excel for xls files.
For example, your loop should look something like this.
ContentType xlsxContent = new ContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
foreach (string file in sentFiles)
{
Attachment attachment = new Attachment(file, xlsxContent);
msg.Attachments.Add(attachment);
}
We use this in our Attachment constructor and have no issues attaching Excel and PDF.
Attachment data = new Attachment(sFileName, MediaTypeNames.Application.Octet);
Also check that the users running this have permissions to access the files in whatever location is specified by sentFilesDK.
You might want to specify the mimetype which is part of one of the constructors on Attachment class.
public Attachment(string fileName, ContentType contentType);
You can also read the file in memorystream and pass it as part of the following constructor.
public Attachment(Stream contentStream, string name, string mediaType);