如何使用 Mailkit 读取电子邮件正文中的内联图像

How to read inline image in email body using Mailkit

我给自己发了一封测试邮件,邮件正文中有一张图片。此图片不是附件,只是粘贴到电子邮件正文中。

我正在使用 MailKit 阅读这封收到的电子邮件,但找不到如何访问该图像。

我正在使用:

MimeMessage message = client.Inbox.GetMessage(uid);

如果我使用 message.ToString(),我可以看到它在那里:

...
Content-Disposition: inline; filename="image001.png"; size=4570;
        creation-date="Mon, 16 Sep 2019 09:21:07 GMT";
        modification-date="Mon, 16 Sep 2019 09:21:07 GMT"
Content-ID: <image001.png@01D56A4B.7CD234E0>
Content-Transfer-Encoding: base64

iVBORw0KGgoAAAANSUhEUgAAAJUAAAAlCAY...

而且我假设 base64 编码的行是实际图像,但我该如何获取它?

编辑

这是我的测试代码:

    static void Main(string[] args)
    {
        ImapClient client = new ImapClient();
        client.Connect("...
        client.Authenticate("...
        client.Inbox.Open(FolderAccess.ReadWrite);
        IList<UniqueId> uids = client.Inbox.Search(SearchQuery.All);
        foreach (UniqueId uid in uids)
        {
            MimeMessage message = client.Inbox.GetMessage(uid);

            IList<IMessageSummary> info = client.Inbox.Fetch(new[] { uid }, MessageSummaryItems.All);

            foreach (MimeEntity me in message.Attachments)
                HandleMimeEntity(me, 1);
        }
        client.Disconnect(true);
    }

    static void HandleMimeEntity(MimeEntity entity)
    {
        int i, j;

        Multipart multipart = entity as Multipart;

        if (multipart != null)
        {
            Console.WriteLine("multipart");
            for (i = 0; i < multipart.Count; i++)
            {
                Console.WriteLine(i + " - " + multipart[i].ContentType.MimeType + " (" + multipart[i].IsAttachment + ")");
                HandleMimeEntity(multipart[i]);
            }
            return;
        }

        MessagePart rfc822 = entity as MessagePart;

        if (rfc822 != null)
        {
            MimeMessage message = rfc822.Message;
            Console.WriteLine("mimemessage - " + message.Subject);

            HandleMimeEntity(message.Body, lvl + 1);
            return;
        }

        MimePart part = (MimePart)entity;
        Console.WriteLine("mimepart - " + part.FileName);

        // do something with the MimePart, such as save content to disk
    }

我真的,真的真的推荐阅读FAQ and/or the README了解 MIME 的结构,以便对此类问题的回答变得显而易见,但与此同时...

首先要理解MIME是一个树形结构,这意味着有一个根节点(可能是也可能不是leaf-node),可以有分支节点(例如multipart/mixedmultipart/alternativemultipart/related 等),还有 leaf-node(例如 text/plaintext/htmlimage/jpegapplication/octet-stream, 等等).

MIME结构的根节点是MimeMessage.Body属性.

如果邮件仅包含一个 text/plain MIME 实体,则 MimeMessage.Body 节点将 成为 那个 text/plain MIME 实体。

在你的情况下,听起来你至少有一个 text/html 实体和一个 image/png 实体。

对您的消息结构一无所知,我们可以得出结论 MimeMessage.Body 既不是 text/html 实体也不是 image/png 实体,因为您只能有 1 个根节点text/*image/* 部分是 leaf-node 实体,而不是分支节点实体。

这意味着 MimeMessage.Body 将是一个 multipart/*,它将由 Multipart(或其子类)表示。

因为很明显你的 MimeMessage.BodyMultipart,我们可以转换它:

var multipart = (Multipart) message.Body;

一旦我们有了 multipart,我们就可以迭代它的 children:

foreach (var child in multipart) {
    // ...
}

此时我们需要弄清楚child是否是另一个Multipart,一个MimePart (which will represent text or image data), or an embedded message (aka MessagePart)。

您可以使用 as 转换来快速推断出它是什么。

然后您只需继续遍历 MIME 实体树,直到找到您正在寻找的内容,这可能需要使用上述方法进行一些递归。

MimeKit 有一些替代方法可以做到这一点,但是:

  1. MimeMessage.BodyParts
  2. MimeIterator
  3. MimeVisitor

这是使用 BodyParts 属性:

快速解决您的问题的方法
var myImage = message.BodyParts.OfType<MimePart> ().FirstOrDefault (x => x.IsMimeType ("image", "png"));