在C#中使用IMAP协议读取某些附件的问题

时间:2018-03-30 17:37:08

标签: c# gmail imap attachment mailkit

我想问你以下问题:

我有两个代码来阅读带有相应附件的电子邮件:

第一个,我使用的是S22.IMAP dll:

using (ImapClient Client = new ImapClient(imap, 993, usuario, psw, AuthMethod.Login, true))
{
    IEnumerable<uint> uids = Client.Search(SearchCondition.Unseen());//Correos no leídos

    IEnumerable<MailMessage> messages = Client.GetMessages(uids, FetchOptions.Normal); 

    conexion.stringconeccion = stringconeccion;
    conexion.conectar();

    String ts = "start transaction";

    MySqlCommand datos_ts = new MySqlCommand(ts, conexion.con);
    datos_ts.ExecuteScalar();

    DataTable dt_existeXML = new DataTable();

    int insercion = 0;

    foreach (MailMessage msg in messages)
    {
        foreach (Attachment atc in msg.Attachments)
        {
            if (System.IO.Path.GetExtension(msg.Attachments[0].Name) == ".xml")
            {
                String archivoXML_texto = "";
                byte[] allBytes = new byte[msg.Attachments[0].ContentStream.Length];
                int bytesRead = msg.Attachments[0].ContentStream.Read(allBytes, 0, (int)msg.Attachments[0].ContentStream.Length);

                using (MemoryStream memory = new MemoryStream(allBytes))
                {
                    StreamReader archivoXML = new StreamReader(memory);

                    archivoXML_texto = archivoXML.ReadToEnd();
                    archivoXML.Close();

                    memory.Dispose();
                }
            }
        }
    }

第二个代码,使用MailKit DLL:

using (var client = new ImapClient ()) {
    client.Connect ("imap.gmail.com", 993, SecureSocketOptions.SslOnConnect);
    client.Authenticate ("correo@gmail.com", "clave");
    client.Inbox.Open (FolderAccess.ReadOnly);

    var uids = client.Inbox.Search(SearchQuery.NotSeen);

    foreach (var uid in uids)
    {
        var message = client.Inbox.GetMessage(uid);
        foreach (var attachment in message.Attachments.OfType<MimePart>())
        {   
                byte[] allBytes = new byte[attachment.Content.Stream.Length];
                int bytesRead = attachment.Content.Stream.Read(allBytes, 0, (int)attachment.Content.Stream.Length);
                string texto_definitivo = "";
                String archivoXML_textoBase64 = "";
                using (MemoryStream memory = new MemoryStream(allBytes))
                {
                    StreamReader archivoXML = new StreamReader(memory);
                    archivoXML_textoBase64 = archivoXML.ReadToEnd();
                    byte[] temp_backToBytes = Convert.FromBase64String(archivoXML_textoBase64);
                    texto_definitivo = Encoding.ASCII.GetString(temp_backToBytes);
                    archivoXML.Close();
                    memory.Dispose();
                }   
        }
    }
    client.Disconnect (true);
}

但是我意识到附件没有读取它们,但是,我注意到了未读取的附件的共同点,没有附件图标。但是当我打开邮件时,我发现他们有附件:

enter image description here

例如,标有红色的图像,右侧的附件图标未显示。但是当我打开它时,我确认它有一个附加的文件。只是那些文件(两个代码中的任何一个)都不会读取附件。

我的问题是:

我怎么能读这些附件? 我应该在邮件中配置或启用任何选项吗?什么怎么样? 发送邮件是错误的吗? 怎么可以解决?

更新

我希望我明白了。以下代码尝试获取附件的文本/内容。我想,如果我错了,请纠正我。

    public static void DownloadBodyParts ()
    {
        using (var client = new ImapClient ()) {
            client.Connect ("imap.gmail.com", 993, SecureSocketOptions.SslOnConnect);             
            client.Authenticate("correo@gmail.com", "clave");
            client.Inbox.Open(FolderAccess.ReadWrite);
            var uids = client.Inbox.Search(SearchQuery.NotSeen);          

            foreach (var uid in uids)
            {
                var message = client.Inbox.GetMessage(uid);                    
                var attachments = message.BodyParts.OfType<MimePart>().Where(part => !string.IsNullOrEmpty(part.FileName));

                foreach (MimePart atch in attachments)
                {
                    using (var memory = new MemoryStream())
                    {
                        atch.Content.DecodeTo(memory);
                        var buffer = memory.ToArray();
                        var text = Encoding.UTF8.GetString(buffer);
                    }
                }
             }
        client.Disconnect (true);
         }

1 个答案:

答案 0 :(得分:0)

来自MailKit FAQ

问:如何判断邮件是否包含附件?

在大多数情况下,包含MIME类型为multipart/mixed且包含多个部分的正文的邮件可能包含附件。如上所示,multipart/mixed的第一部分通常是消息的文本主体,但并不总是那么简单。

通常,MIME附件的标头值为Content-Disposition。要获取符合此条件的身体部位列表,您可以使用MimeMessage.Attachments属性。

不幸的是,并非所有邮件客户端都遵循此约定,因此您可能需要编写自己的自定义逻辑。例如,您可能希望对其上设置了namefilename参数的所有身体部位进行处理:

var attachments = message.BodyParts.OfType<MimePart> ().Where (part => !string.IsNullOrEmpty (part.FileName));

更复杂的方法是将未被消息的主要文本正文部分引用的身体部位视为附件。换句话说,将未用于呈现消息的任何身体部位视为附件。有关如何执行此操作的示例,请考虑以下代码段:

/// <summary>
/// Visits a MimeMessage and generates HTML suitable to be rendered by a browser control.
/// </summary>
class HtmlPreviewVisitor : MimeVisitor
{
    List<MultipartRelated> stack = new List<MultipartRelated> ();
    List<MimeEntity> attachments = new List<MimeEntity> ();
    readonly string tempDir;
    string body;

    /// <summary>
    /// Creates a new HtmlPreviewVisitor.
    /// </summary>
    /// <param name="tempDirectory">A temporary directory used for storing image files.</param>
    public HtmlPreviewVisitor (string tempDirectory)
    {
        tempDir = tempDirectory;
    }

    /// <summary>
    /// The list of attachments that were in the MimeMessage.
    /// </summary>
    public IList<MimeEntity> Attachments {
        get { return attachments; }
    }

    /// <summary>
    /// The HTML string that can be set on the BrowserControl.
    /// </summary>
    public string HtmlBody {
        get { return body ?? string.Empty; }
    }

    protected override void VisitMultipartAlternative (MultipartAlternative alternative)
    {
        // walk the multipart/alternative children backwards from greatest level of faithfulness to the least faithful
        for (int i = alternative.Count - 1; i >= 0 && body == null; i--)
            alternative[i].Accept (this);
    }

    protected override void VisitMultipartRelated (MultipartRelated related)
    {
        var root = related.Root;

        // push this multipart/related onto our stack
        stack.Add (related);

        // visit the root document
        root.Accept (this);

        // pop this multipart/related off our stack
        stack.RemoveAt (stack.Count - 1);
    }

    // look up the image based on the img src url within our multipart/related stack
    bool TryGetImage (string url, out MimePart image)
    {
        UriKind kind;
        int index;
        Uri uri;

        if (Uri.IsWellFormedUriString (url, UriKind.Absolute))
            kind = UriKind.Absolute;
        else if (Uri.IsWellFormedUriString (url, UriKind.Relative))
            kind = UriKind.Relative;
        else
            kind = UriKind.RelativeOrAbsolute;

        try {
            uri = new Uri (url, kind);
        } catch {
            image = null;
            return false;
        }

        for (int i = stack.Count - 1; i >= 0; i--) {
            if ((index = stack[i].IndexOf (uri)) == -1)
                continue;

            image = stack[i][index] as MimePart;
            return image != null;
        }

        image = null;

        return false;
    }

    // Save the image to our temp directory and return a "file://" url suitable for
    // the browser control to load.
    // Note: if you'd rather embed the image data into the HTML, you can construct a
    // "data:" url instead.
    string SaveImage (MimePart image, string url)
    {
        string fileName = url.Replace (':', '_').Replace ('\\', '_').Replace ('/', '_');

        string path = Path.Combine (tempDir, fileName);

        if (!File.Exists (path)) {
            using (var output = File.Create (path))
                image.Content.DecodeTo (output);
        }

        return "file://" + path.Replace ('\\', '/');
    }

    // Replaces <img src=...> urls that refer to images embedded within the message with
    // "file://" urls that the browser control will actually be able to load.
    void HtmlTagCallback (HtmlTagContext ctx, HtmlWriter htmlWriter)
    {
        if (ctx.TagId == HtmlTagId.Image && !ctx.IsEndTag && stack.Count > 0) {
            ctx.WriteTag (htmlWriter, false);

            // replace the src attribute with a file:// URL
            foreach (var attribute in ctx.Attributes) {
                if (attribute.Id == HtmlAttributeId.Src) {
                    MimePart image;
                    string url;

                    if (!TryGetImage (attribute.Value, out image)) {
                        htmlWriter.WriteAttribute (attribute);
                        continue;
                    }

                    url = SaveImage (image, attribute.Value);

                    htmlWriter.WriteAttributeName (attribute.Name);
                    htmlWriter.WriteAttributeValue (url);
                } else {
                    htmlWriter.WriteAttribute (attribute);
                }
            }
        } else if (ctx.TagId == HtmlTagId.Body && !ctx.IsEndTag) {
            ctx.WriteTag (htmlWriter, false);

            // add and/or replace oncontextmenu="return false;"
            foreach (var attribute in ctx.Attributes) {
                if (attribute.Name.ToLowerInvariant () == "oncontextmenu")
                    continue;

                htmlWriter.WriteAttribute (attribute);
            }

            htmlWriter.WriteAttribute ("oncontextmenu", "return false;");
        } else {
            // pass the tag through to the output
            ctx.WriteTag (htmlWriter, true);
        }
    }

    protected override void VisitTextPart (TextPart entity)
    {
        TextConverter converter;

        if (body != null) {
            // since we've already found the body, treat this as an attachment
            attachments.Add (entity);
            return;
        }

        if (entity.IsHtml) {
            converter = new HtmlToHtml {
                HtmlTagCallback = HtmlTagCallback
            };
        } else if (entity.IsFlowed) {
            var flowed = new FlowedToHtml ();
            string delsp;

            if (entity.ContentType.Parameters.TryGetValue ("delsp", out delsp))
                flowed.DeleteSpace = delsp.ToLowerInvariant () == "yes";

            converter = flowed;
        } else {
            converter = new TextToHtml ();
        }

        body = converter.Convert (entity.Text);
    }

    protected override void VisitTnefPart (TnefPart entity)
    {
        // extract any attachments in the MS-TNEF part
        attachments.AddRange (entity.ExtractAttachments ());
    }

    protected override void VisitMessagePart (MessagePart entity)
    {
        // treat message/rfc822 parts as attachments
        attachments.Add (entity);
    }

    protected override void VisitMimePart (MimePart entity)
    {
        // realistically, if we've gotten this far, then we can treat this as an attachment
        // even if the IsAttachment property is false.
        attachments.Add (entity);
    }
}
And the way you'd use this visitor might look something like this:

void Render (MimeMessage message)
{
    var tmpDir = Path.Combine (Path.GetTempPath (), message.MessageId);
    var visitor = new HtmlPreviewVisitor (tmpDir);

    Directory.CreateDirectory (tmpDir);

    message.Accept (visitor);

    DisplayHtml (visitor.HtmlBody);
    DisplayAttachments (visitor.Attachments);
}

使用上述技术渲染邮件后,您将获得未使用的附件列表,即使它们与MimeMessage.Attachments属性使用的简单标准不匹配也是如此

<强>更新

以下是如何获取MimePart的文本内容(假设MimePart无法转换为TextPart,这会使此过程变得简单)。

using (var memory = new MemoryStream ()) {
    mimePart.Content.DecodeTo (memory);

    var buffer = memory.ToArray ();
    var text = Encoding.UTF8.GetString (buffer);
}

当然,如果MimePart可以投放到TextPart,那么这更简单:

var textPart = (TextPart) mimePart;
var text = textPart.Text;