【问题标题】:Display embed images in HTML emails在 HTML 电子邮件中显示嵌入图像
【发布时间】:2019-02-02 01:28:12
【问题描述】:

我通过 Mailkit IMAP 将电子邮件存储为 .eml 文件,当用户想要打开邮件时,我打开该 eml 文件并显示电子邮件的内容和附件

var file = System.IO.File.ReadAllBytes("E\\sampleEmail.eml"));
var msg = new MsgReader.Mime.Message(file);
ViewBag.Body = System.Text.Encoding.UTF8.GetString(msg.HtmlBody == null ? msg.TextBody.Body : msg.HtmlBody.Body);

电子邮件的 HtmlBody 包含所有内容,但我如何在 html 上显示嵌入图像?

img 标签显示如下,并且有 cid:

<img src="cid:image001.png@01d4330b.ed93f2e0" id="_x0000_i1025">

image001 是 eml 文件的附件,它嵌入在电子邮件正文的 html 内容中,直接在它们所在的位置显示图像的最佳方法是什么?!

【问题讨论】:

    标签: c# asp.net-mvc html-email mailkit


    【解决方案1】:

    如果您使用 MailKit 保存从 IMAP 下载的邮件,那么您可以像这样重新加载邮件:

    var message = MimeMessage.Load (fileName);
    

    然后,要获取消息正文,您可以这样做:

    string body = message.HtmlBody ?? message.TextBody;
    

    现在,要回答有关如何使用 MimeKit/MailKit 渲染图像的问题,您可以在此处查看示例消息查看器:https://github.com/jstedfast/MimeKit/tree/master/samples/MessageReader/MessageReader

    重要的是HtmlPreviewVisitor:

    using System;
    using System.IO;
    using System.Collections.Generic;
    
    using MimeKit;
    using MimeKit.Text;
    using MimeKit.Tnef;
    
    namespace MessageReader
    {
        /// <summary>
        /// Visits a MimeMessage and generates HTML suitable to be rendered by a browser control.
        /// </summary>
        class HtmlPreviewVisitor : MimeVisitor
        {
            readonly List<MultipartRelated> stack = new List<MultipartRelated> ();
            readonly List<MimeEntity> attachments = new List<MimeEntity> ();
            string body;
    
            /// <summary>
            /// Creates a new HtmlPreviewVisitor.
            /// </summary>
            public HtmlPreviewVisitor ()
            {
            }
    
            /// <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;
            }
    
            /// <summary>
            /// Gets the attachent content as a data URI.
            /// </summary>
            /// <returns>The data URI.</returns>
            /// <param name="attachment">The attachment.</param>
            string GetDataUri (MimePart attachment)
            {
                using (var memory = new MemoryStream ()) {
                    attachment.Content.DecodeTo (memory);
                    var buffer = memory.GetBuffer ();
                    var length = (int) memory.Length;
                    var base64 = Convert.ToBase64String (buffer, 0, length);
    
                    return string.Format ("data:{0};base64,{1}", attachment.ContentType.MimeType, base64);
                }
            }
    
            // 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 = GetDataUri (image);
    
                            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);
            }
        }
    }
    

    将上述代码与 System.Windows.Forms.WebBrowser 一起使用的方法是:

    void Render ()
    {
        var visitor = new HtmlPreviewVisitor ();
        message.Accept (visitor);
    
        webBrowser.DocumentText = visitor.HtmlBody;
    }
    

    HtmlPreviewVisitor 所做的是使用&lt;img&gt; 标签中的“data:” URL 方案将图像附件嵌入到 HTML 中。

    【讨论】:

    • 太棒了。非常感谢
    【解决方案2】:
    MailMessage mailWithImg = getMailWithImg();
    MySMTPClient.Send(mailWithImg); //* Set up your SMTPClient before!
    
    private MailMessage getMailWithImg() {
        MailMessage mail = new MailMessage();
        mail.IsBodyHtml = true;
        mail.AlternateViews.Add(getEmbeddedImage("c:/image.png"));
        mail.From = new MailAddress("yourAddress@yourDomain");
        mail.To.Add("recipient@hisDomain");
        mail.Subject = "yourSubject";
        return mail;
    }
    private AlternateView getEmbeddedImage(String filePath) {
        LinkedResource res = new LinkedResource(filePath);
        res.ContentId = Guid.NewGuid().ToString();
        string htmlBody = @"<img src='cid:" + res.ContentId + @"'/>";
        AlternateView alternateView = AlternateView.CreateAlternateViewFromString(htmlBody, null, MediaTypeNames.Text.Html);
        alternateView.LinkedResources.Add(res);
        return alternateView;
    }
    

    【讨论】:

    • 我想显示那个不生成
    【解决方案3】:

    我使用一个名为 MSGReader 的库。

    Git: https://github.com/Sicos1977/MSGReader

    一般规则: 这个 id: "cid:image001.png@01d4330b.ed93f2e0" 是指向文件末尾的链接,图像的存储方式类似于 base64。我对 MSGReader 做了一些更改,我会尽快提出拉取请求,对我来说效果很好。

    【讨论】:

    • 您是否提出了这个要求,这也是我们需要的。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2011-10-06
    • 2015-03-14
    • 2020-08-20
    • 2010-12-03
    • 2020-12-14
    • 2015-02-17
    • 2016-11-06
    相关资源
    最近更新 更多