I'm downloading attachments for a given mailid with the following code:
HeaderSearchQuery searchCondition = SearchQuery.HeaderContains("Message-Id", ssMailItemId);
var folder = client.GetFolder(ssFolderName);
folder.Open(FolderAccess.ReadOnly);
IList<UniqueId> ids = folder.Search(searchCondition);
foreach (UniqueId uniqueId in ids)
{
MimeMessage message = folder.GetMessage(uniqueId);
foreach (MimeEntity attachment in message.Attachments)
{
ssAttachmentsDetail.Append(fillAttachmentDetailRecord(attachment, uniqueId.Id.ToString()));
}
But both the MimeEntity.ContentDisposition.Size and MimePart.ContentDuration are null. Is there any field regarding the size of the attachment?
The ContentDisposition.Size property is only set if the Content-Disposition header has a size parameter, like this:
Content-Disposition: attachment; size=15462
But that value should probably not really be trusted anyway...
If you want the size of an attachment, the only accurate way of doing this would be to do something like this:
// specify that we want to fill the IMessageSummary.Body and IMessageSummary.UniqueId fields...
var items = folder.Fetch (ids, MessageSummaryItems.BodyStructure | MessageSummaryItems.UniqueId);
foreach (var item in items) {
foreach (var attachment in item.Attachments) {
// 'octets' is just a fancy word for "number of bytes"
var size = attachment.Octets;
// download the individual attachment
var entity = folder.GetBodyPart (item.UniqueId, attachment);
}
}
Related
In my application users can upload emails to a file server, these emails should then be stripped of their attachments and converted into a PDF to be saved individually. But I'm having problems correctly removing the attachments from the email.
When I'm converting an email and saving it with its attachments it works perfectly, but when I remove the attachments first and then save it, it somehow corrupts the generated PDF file making it look like this (this is just one of 4 pages generated from a 4 line email). Can anyone explain what I'm doing wrong?
This is my code to remove the attachments from the email:
public static List<(string FileName, Stream Content)> GetEmailAndAttachmentsFromEmail(Stream emailContent)
{
var email = MailMessage.Load(emailContent);
var retval = new List<(string, Stream)>
{
($"{email.Subject}.msg", emailContent)
};
var attachmentsToRemove = new List<Attachment>();
foreach (var attachment in email.Attachments)
{
retval.Add((attachment.Name, attachment.ContentStream));
attachmentsToRemove.Add(attachment);
}
foreach (var attachment in attachmentsToRemove)
{
email.Attachments.Remove(attachment);
}
return retval;
}
I've already tried multiple permutations of this code, but none worked.
Also, I'm following the official Aspose documentation on this subject and I don't see what I'm doing differently/ wrong.
It turns out I did something funky with my streams and I had to save my email without the attachments before returning it, here is my revised code:
public static List<(string FileName, Stream Content)> GetEmailAndAttachmentsFromEmail(Stream emailContent)
{
var email = MailMessage.Load(emailContent);
// I removed the prepending of the email here and moved it to the end
var retval = new List<(string, Stream)>();
var attachmentsToRemove = new List<Attachment>();
foreach (var attachment in email.Attachments)
{
retval.Add((attachment.Name, attachment.ContentStream));
attachmentsToRemove.Add(attachment);
}
foreach (var attachment in attachmentsToRemove)
{
email.Attachments.Remove(attachment);
}
// This part is new
var newEmailContent = new MemoryStream();
email.Save(newEmailContent);
newEmailContent.Seek(0, SeekOrigin.Begin);
retval = retval.Prepend(($"{email.Subject}.msg", newEmailContent)).ToList();
return retval;
}
It now works like a charm
Is it possible to save file attachments in C# through Microsoft Graph API?
I know I can get the properties of the attachment (https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/attachment_get) - can we also save it to a certain location?
Once you have particular Microsoft Graph message, you can e.g. pass it to a method as parameter. Then you need to make another request to get attachments by message Id, iterate through attachments and cast it to FileAttachment to get access to the ContentBytes property and finally save this byte array to the file.
private static async Task SaveAttachments(Message message)
{
var attachments =
await _client.Me.MailFolders.Inbox.Messages[message.Id].Attachments.Request().GetAsync();
foreach (var attachment in attachments.CurrentPage)
{
if (attachment.GetType() == typeof(FileAttachment))
{
var item = (FileAttachment)attachment; // Cast from Attachment
var folder = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
var filePath = Path.Combine(folder, item.Name);
System.IO.File.WriteAllBytes(filePath, item.ContentBytes);
}
}
}
When you get the attachment properties, they will contain information about the attachment.
There are three types of attachments.
First, check the attachment type in the properties' #odata.type and handle them correspondingly.
For fileAttachment types, they contain a contentLocation attribute, which is the URI of the attachment contents.
You can download the attachment from the URI.
I'm trying to learn how to use the MailKit library but I am struggling to retrieve attachments. So far my code will open a mailbox, go through each message and store data such as sender, subject, body, date etc. but I can't deal with attachments.
I have tried to use other peoples solutions found on here, on github and other sites but I still don't understand exactly what they are doing in their code and when I come close to getting a solution working it causes more bugs so I get stressed and delete all the code. I don't mean to seem lazy but I would love if somebody could explain how I can achieve this. I'm basically trying to build a mail client for a web forms app.
Below is my code, so as you can see I'm fairly clueless :)
// Open the Inbox folder
client.Inbox.Open(FolderAccess.ReadOnly, cancel.Token);
//get the full summary information to retrieve all details
var summary = client.Inbox.Fetch(0, -1, MessageSummaryItems.Full, cancel.Token);
foreach (var msg in summary)
{
//this code originally downloaded just the text from the body
var text = msg.Body as BodyPartText;
//but I tried altering it so that it will get attachments here also
var attachments = msg.Body as BodyPartBasic;
if (text == null)
{
var multipart = msg.Body as BodyPartMultipart;
if (multipart != null)
{
text = multipart.BodyParts.OfType<BodyPartText>().FirstOrDefault();
}
}
if (text == null)
continue;
//I hoped this would get the messages where the content dispositon was not null
//and let me do something like save the attachments somewhere but instead it throws exceptions
//about the object reference not set to an instance of the object so it's very wrong
if (attachments.ContentDisposition != null && attachments.ContentDisposition.IsAttachment)
{
//I tried to do the same as I did with the text here and grab the body part....... but no
var attachedpart = client.Inbox.GetBodyPart(msg.Index, attachments, cancel.Token);
}
else
{
//there is no plan b :(
}
// this will download *just* the text
var part = client.Inbox.GetBodyPart(msg.Index, text, cancel.Token);
//cast main body text to Text Part
TextPart _body = (TextPart)part;
I'm not entirely clear on what you want to accomplish, but if you just want to download the message attachments (without downloading the entire message) and save those attachments to the file system, here's how you can accomplish that:
var messages = client.Inbox.Fetch (0, -1, MessageSummaryItems.Full | MessageSummaryItems.UniqueId);
int unnamed = 0;
foreach (var message in messages) {
var multipart = message.Body as BodyPartMultipart;
var basic = message.Body as BodyPartBasic;
if (multipart != null) {
foreach (var attachment in multipart.BodyParts.OfType<BodyPartBasic> ().Where (x => x.IsAttachment)) {
var mime = (MimePart) client.Inbox.GetBodyPart (message.UniqueId.Value, attachment);
var fileName = mime.FileName;
if (string.IsNullOrEmpty (fileName))
fileName = string.Format ("unnamed-{0}", ++unnamed);
using (var stream = File.Create (fileName))
mime.ContentObject.DecodeTo (stream);
}
} else if (basic != null && basic.IsAttachment) {
var mime = (MimePart) client.Inbox.GetBodyPart (message.UniqueId.Value, basic);
var fileName = mime.FileName;
if (string.IsNullOrEmpty (fileName))
fileName = string.Format ("unnamed-{0}", ++unnamed);
using (var stream = File.Create (fileName))
mime.ContentObject.DecodeTo (stream);
}
}
Another alternative that works for me, but appears to be a little simpler:
var messages = client.Inbox.Fetch (0, -1, MessageSummaryItems.Full | MessageSummaryItems.BodyStructure | MessageSummaryItems.UniqueId);
int unnamed = 0;
foreach (var message in messages) {
foreach (var attachment in message.Attachments) {
var mime = (MimePart) client.Inbox.GetBodyPart (message.UniqueId.Value, attachment);
var fileName = mime.FileName;
if (string.IsNullOrEmpty (fileName))
fileName = string.Format ("unnamed-{0}", ++unnamed);
using (var stream = File.Create (fileName))
mime.ContentObject.DecodeTo (stream);
}
}
Note that this is asking for the BODYSTRUCTURE instead of the BODY in the Fetch statement, which seems to fix the issue of attachments not being flagged as such.
I am trying to access the file attachment in the email message and upload the attachment to a azure table storage as a blob.
using Microsoft.Exchange.WebServices.Data
public void SendEmail(EmailMessage emailMessage)
{Stream stream = null;
foreach (Attachment attachment in emailMessage.Attachments)
{
if (attachment is FileAttachment)
{
FileAttachment file = (FileAttachment)attachment;
file.Load(stream);
emailAttachment.UploadEmailAttachment(attachmentFileName, stream);// This will upload to the table storage
}
}
}
when I load the attachment I am getting a error saying "The request failed schema validation: The required attribute 'Id' is missing.".
Any idea regarding this
As I understand, all you are looking for a way to load the contents of the attachment into a stream which you can further upload as blob.
If that is the case, I would suggest you to write the contents of your file attachment into MemoryStream instead:
var stream = new System.IO.MemoryStream(fileAttachment.Content);
If you want to read the contents as string, you can do that as well:
var reader = new System.IO.StreamReader(stream, UTF8Encoding.UTF8);
var text = reader.ReadToEnd();
Hope this helps!
I have the following code that basically attaches a file to an email message then after all attachments are attached and email is sent, i try to delete all files, however I get a file in use exception. I believe the error comes in this line
Attachment data = new Attachment(file, MediaTypeNames.Application.Octet);
I tried using this code but I get an cannot sent email message
using Attachment data = new Attachment(file, MediaTypeNames.Application.Octet)){
//and the rest of the code in here.
}
foreach (KeyValuePair<string, string> kvp in reports) {
browser.GoTo(kvp.Value);
Thread.Sleep(1000);
System.IO.File.Move(#"C:\Reports\bidata.csv", #"C:\Reports\"+kvp.Key.ToString()+".csv");
string file = #"C:\Reports\" + kvp.Key.ToString() + ".csv";
Attachment data = new Attachment(file, MediaTypeNames.Application.Octet);
// Add time stamp information for the file.
ContentDisposition disposition = data.ContentDisposition;
disposition.CreationDate = System.IO.File.GetCreationTime(file);
disposition.ModificationDate = System.IO.File.GetLastWriteTime(file);
disposition.ReadDate = System.IO.File.GetLastAccessTime(file);
// Add the file attachment to this e-mail message.
mail.Attachments.Add(data);
}
smtpserver.Send(mail);
string[] files = Directory.GetFiles(#"C:\Reports");
foreach (string files1 in files)
{
File.Delete(files1);
}
In order to delete the files first you will have to dispose the attachment and mail objects and then delete the files
Dispose the smtpclient by putting it in a usings or calling dispose directly. That should free the file resource and allow you to nuke it.