2014-11-25 3 views
1

Я использую код от here для загрузки вложений из почты с помощью MailKit. В цикле foreach, где извлекаются вложения, он всегда возвращается пустым. Поскольку он пуст, он не входит в цикл foreach. Пожалуйста, поправьте меня, если я что-то сделаю неправильно.Как загрузить вложения электронной почты с помощью Mailkit

 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) 
      { 

       //Here the attachments is always empty even though my mail has email with attachements 
       var attachments = multipart.BodyParts.OfType<BodyPartBasic>().Where(x => x.IsAttachment); 
       foreach (var attachment in 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); 
       } 
      } 
      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); 
      } 
     } 

ответ

0

Первое, что нужно отметить, это MIME - это древовидная структура, а не плоский список. BodyPartMultipart может содержать другие дети BodyPartMultipart, а также дети BodyPartBasic, поэтому, когда вы сталкиваетесь с другим BodyPartMultipart, вам также необходимо спуститься в это.

Я предполагаю, что сообщения, которые вы изучаете, имеют гнездовые множественные соединения, и именно поэтому у вас возникают проблемы.

Либо это, либо «вложения» не имеют заголовок Content-Disposition со значением «вложение».

+0

Можете ли вы предоставить мне какой-либо образец или обходной путь для загрузки вложений по описанному выше процессу. Я новичок в структуре дерева MIME, я попытаюсь просмотреть ваши документы. – mahindar

2

Вот что я использую, чтобы пересечь структуру дерева MIME, чтобы получить все возможные вложения. Обратите внимание, что, по крайней мере, в случае писем, которые я тестировал, флаг IsAttachment никогда не был установлен в true.

Во-первых, сделать рекурсивную функцию, которая извлекает все основные части тела (которые являются частями сообщения MIME, который содержит фактическое содержание сообщения) как IEnumerable<BodyPartBasic>:

private static IEnumerable<BodyPartBasic> GetBasicBodyParts(this BodyPart part) 
{ 
    var multipart = part as BodyPartMultipart; 
    var basic = part as BodyPartBasic; 
    if (multipart != null) 
    { 
     foreach (var subPart in multipart.BodyParts) 
     { 
      if (subPart is BodyPartBasic) 
      { 
       yield return subPart as BodyPartBasic; 
      } 
      else 
      { 
       foreach (var subSubPart in subPart.GetBasicBodyParts()) 
       { 
        yield return subSubPart; 
       } 
      } 
     } 
    } else if (basic != null) 
    { 
     yield return basic; 
    } 
    else 
    { 
     yield break; 
    } 
} 

А затем поставить эту функцию использовать в функции «загрузки», который очень похож на то, что @jstedfast предоставил в других постах:

public static IEnumerable<FileInfo> DownloadAttachments(this EmailClient client, IMessageSummary message, 
    string destination) 
{ 
    fileNameFilter = fileNameFilter ?? AlwaysTrue; 
    if (!Directory.Exists(destination)) 
     Directory.CreateDirectory(destination); 

    var folder = client.Inbox; 
    folder.Open(FolderAccess.ReadOnly); 
    foreach (var part in message.Body.GetBasicBodyParts()) 
    { 

     var mimeHeader = (MimePart) folder.GetBodyPart(message.UniqueId.Value, part, headersOnly: true); 
     var headerFileName = mimeHeader.FileName; 
     if (string.IsNullOrWhiteSpace(headerFileName)) 
      continue; 


     var mime = (MimePart) folder.GetBodyPart(message.UniqueId.Value, part); 
     var fileName = mime.FileName; 

     var filePath = Path.Combine(destination, fileName); 
     using (var stream = File.Create(filePath)) 
      mime.ContentObject.DecodeTo(stream); 

     yield return new FileInfo(filePath); 
    } 
} 

Обратите внимание, что эта функция:

  • Пропускает файлы, у которых нет имени файла. Если вы хотите сохранить эти файлы, вы можете добавить счетчик unnamed из других примеров @ jstedfast. Вы также можете добавить в другую логику фильтрацию файлов на основе имени файла или другой информации, которую вы получите в заголовке MIME.
  • Сначала загружается только заголовок приложения. Это позволяет фильтровать имена файлов, не загружая контент в первую очередь.
  • Является ли метод расширения и может поэтому использоваться как: client.DownloadAttachments(someMessage, @"C:\tmp\attachments")
  • Возвращает System.IO.FileInfo объектов для каждого прикрепленного файла. Это было полезно для меня, но может не иметь отношения к другим. Это не обязательно.

Я надеюсь, что это поможет другим людям.