mimemessage 发送html,MimeKit.MimeMessage到浏览器可渲染的HTML

jstedfast..

7

您的解决方案类似于我在MimeKit的MessageReader示例中使用的逻辑,但现在MimeKit提供了更好的解决方案:

///

/// Visits a MimeMessage and generates HTML suitable to be rendered by a browser control.

///

class HtmlPreviewVisitor : MimeVisitor

{

List stack = new List ();

List attachments = new List ();

readonly string tempDir;

string body;

///

/// Creates a new HtmlPreviewVisitor.

///

/// A temporary directory used for storing image files.

public HtmlPreviewVisitor (string tempDirectory)

{

tempDir = tempDirectory;

}

///

/// The list of attachments that were in the MimeMessage.

///

public IList Attachments {

get { return attachments; }

}

///

/// The HTML string that can be set on the BrowserControl.

///

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.ContentObject.DecodeTo (output);

}

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

}

// Replaces ... 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 οncοntextmenu="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 ();

}

string text = entity.Text;

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

}

}

然后要使用这个自定义HtmlPreviewVisitor类,你有一个像这样的方法:

void Render (WebBrowser browser, MimeMessage message)

{

var tmpDir = Path.Combine (Path.GetTempPath (), message.MessageId);

var visitor = new HtmlPreviewVisitor (tmpDir);

Directory.CreateDirectory (tmpDir);

message.Accept (visitor);

browser.DocumentText = visitor.HtmlBody;

}

我知道这看起来像很多代码,但它覆盖的不仅仅是简单的案例.您会注意到,如果HTML不可用,它还会处理渲染text/plain和text/plain; format=flowed正文.它也正确地仅使用作为封装multipart/related树一部分的图像.

修改此代码的一种方法是将图像嵌入到img标记中,而不是使用临时目录.要做到这一点,你要修改SaveImage方法是这样的(要注意,下一段代码未经测试):

string SaveImage (MimePart image, string url)

{

using (var output = new MemoryStream ()) {

image.ContentObject.DecodeTo (output);

var buffer = output.GetBuffer ();

int length = (int) output.Length;

return string.Format ("data:{0};base64,{1}", image.ContentType.MimeType, Convert.ToBase64String (buffer, 0, length));

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值