I'm currently developing an Outlook Addin which saves MailItems and Attachments in my MSSQL Database.
I got a method where I save the MailItem with all it's attachments. But if I save all attachments the embedded images in the MailItem are also saved.
Does anyone know how to save all real attachments?? I mean like the attachments in the picture below:
and not the embbeded images that are in the mail body.
Here is the code that I use to loop through all attachments of a MailItem and then save it:
foreach (Outlook.Attachment att in mailItem.Attachments)
{
try
{
att.SaveAsFile(Path.GetTempPath() + att.FileName);
var fi = new FileInfo(Path.GetTempPath() + att.FileName);
//Saving attachment to DB
var attachment = Attachment.NieuwAttachment(att.FileName, SelectedMap.DossierNr.ToString( CultureInfo.InvariantCulture), -1, Convert.ToInt32(SelectedMap.Tag), fi);
if (!Attachment.InlezenAttachment(attachment)) continue;
OutlookCategories.AddAttachmentCategory(mailItem);
}
catch (Exception ex)
{
var dmsEx = new DmsException("Er is een fout opgetreden bij het opslaan van een bijlage.", ex.Message, ex);
ExceptionLogger.LogError(dmsEx);
}
}
Thanks!
----------- EDIT ------------
I also posted this question on the Microsoft TechNet and I just received an answer to the question (See link below)
Outlook 2007 & 2010: Save all attachments except the embedded attachments C#
----------- EDIT ------------
My problem is still not fixed, the help I got from Microsoft is useless.. So Please I really need this to be fixed!
Use this code answered here :
if (mailItem.Attachments.Count > 0)
{
// get attachments
foreach (Attachment attachment in mailItem.Attachments)
{
var flags = attachment.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x37140003");
//To ignore embedded attachments -
if (flags != 4)
{
// As per present understanding - If rtF mail attachment comes here - and the embeded image is treated as attachment then Type value is 6 and ignore it
if ((int)attachment.Type != 6)
{
MailAttachment mailAttachment = new MailAttachment { Name = attachment.FileName };
mail.Attachments.Add(mailAttachment);
}
}
}
}
Depends on how you define 'real' or 'proper' attachments. I'm going to assume you want to disregard all images that are embedded in the email. These are also attachments but are referenced in the actual body of the html email.
See this answer for an explanation on how attachments are embedded. The key is to disregard attachments that have a Content-ID value that is referenced by an image tag within the body of the email itself.
This worked for me:
var test = attachments[i].PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001E");
if (string.IsNullOrEmpty((string)test))
{
//attachment
}
else
{
//embedded image
}
Related
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;
}
}
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);
}
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);
I need to get and save the attachments(s) from a mail item, but using the code below returns all attachments - meaning it also returns the embedded images like the sender's signature with logo which is an image. How can I differentiate a true attachment vs. embedded images? I have seen a lot from forums but it is still unclear to me.
public static void SaveData(MailItem currentMailItem)
{
if (currentMailItem != null)
{
if (currentMailItem.Attachments.Count > 0)
{
for (int i = 1; i <= currentMailItem.Attachments.Count; i++)
{
currentMailItem.Attachments[i].SaveAsFile(#"C:\TestFileSave\" + currentMailItem.Attachments[i].FileName);
}
}
}
}
You can check whether an attachment is inline or not by using the following pseudo-code from MS Technet Forums.
if body format is plain text then
no attachment is inline
else if body format is RTF then
if PR_ATTACH_METHOD value is 6 (ATTACH_OLE) then
attachment is inline
else
attachment is normal
else if body format is HTML then
if PR_ATTACH_FLAGS value has the 4 bit set (ATT_MHTML_REF) then
attachment is inline
else
attachment is normal
You can access the message body format using MailItem.BodyFormat and the MIME attachment properties using Attachment.PropertyAccessor.
string PR_ATTACH_METHOD = 'http://schemas.microsoft.com/mapi/proptag/0x37050003';
var attachMethod = attachment.PropertyAccessor.Get(PR_ATTACH_METHOD);
string PR_ATTACH_FLAGS = 'http://schemas.microsoft.com/mapi/proptag/0x37140003';
var attachFlags = attachment.PropertyAccessor.Get(PR_ATTACH_FLAGS);
Up to know i can load the attachments to memory and i know its right cause i can print the name of the file. What i need is to convert this attachment to an image object which i will later add to a sharepoint picture library. But forget about the sharepoint part i know how to do that, am stuck in the part that after loading the attachments how do i conver this into images. I dont want to save the images in disk cause thats not the point i already load them in memory.
foreach (Item item in findResults.Items)
{
if (item is EmailMessage && item.HasAttachments)
{
EmailMessage message = EmailMessage.Bind(service, item.Id, new PropertySet(BasePropertySet.IdOnly, ItemSchema.Attachments));
foreach (Attachment attachment in message.Attachments)
{
if (attachment is FileAttachment)
{
FileAttachment fileAttachment = attachment as FileAttachment;
// Load the file attachment into memory and print out its file name.
fileAttachment.Load();
Console.WriteLine("Attachment name: " + fileAttachment.Name);
//this is where i would create the image of object but dont know how
}
}
}
}
You already have the FileAttachment object, and you even access one of its properties. You only need to take the next step, and access not only the Name but also the Content.
if (attachment is FileAttachment)
{
FileAttachment fileAttachment = attachment as FileAttachment;
fileAttachment.Load();
byte[] fileContent = fileAttachment.Content;
}
This will give you the contents on the attachemnts, as an array of bytes. I don't remember what the Sharepoint API wants to receive, but it's either this byte array or something you can easily build out of it.