Показывать вставлять изображения в электронные письма HTML
Я сохраняю электронные письма в виде файлов.eml с помощью Mailkit IMAP, и когда пользователь хочет открыть почту, я открываю этот файл 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-контент тела письма, каков наилучший способ отобразить изображения прямо там, где они есть?!
3 ответа
Если вы сохраняете сообщения, загруженные из IMAP с помощью MailKit, вы можете перезагрузить сообщение следующим образом:
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, так это вставляет вложения изображений в HTML, используя схему URL "data:" в <img>
теги.
Я использую библиотеку с именем MSGReader.
Git: https://github.com/Sicos1977/MSGReader
Общие правила: этот идентификатор: "cid:image001.png@01d4330b.ed93f2e0" является ссылкой на конец файла, изображения хранятся как base64. Я сделал некоторые изменения в MSGReader, я сделаю пулл-запрос как можно скорее, у меня все получилось очень хорошо.
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;
}