Прежде чем начать, я должен отметить, что вы не можете надежно использовать Encoding.UTF8.GetBytes()
на client.GetMessage(0).RawMessage
, потому что (ошибочно) предполагает, что данные необработанных сообщений находятся в UTF-8, и это не гарантируется. Большинство электронных писем в Западной Европе будут в одном из латинских кодировок, русские электронные письма, как правило, будут в iso-8859-5, windows-1251 или koi8-r. Китайская, японская и корейская электронная почта почти всегда будут в одном из своих языковых кодировок (обычно gbk/big5, iso-2022-jp и euc-kr соответственно).
Теперь, когда это разрешено ... давайте перейдем к вашему основному вопросу.
То, что вы хотите сделать, это довольно откровенно намного проще сделать с помощью MailKit, чем при использовании OpenPOP, поэтому я покажу вам, как сделать это с помощью MailKit вместо:
Чтобы получить сообщение, MailKit работы очень похоже OpenPOP делает:
using System;
using MailKit.Net.Pop3;
using MailKit;
using MimeKit;
namespace TestClient {
class Program
{
public static void Main (string[] args)
{
using (var client = new Pop3Client()) {
client.Connect ("pop.gmail.com", 995, true);
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove ("XOAUTH2");
client.Authenticate ("[email protected]", "password");
for (int i = 0; i < client.Count; i++) {
var message = client.GetMessage (i);
// TODO: render the message
}
client.Disconnect (true);
}
}
}
}
оказывать сообщение, используя MailKit, самый простой способ, чтобы написать свой собственный MimeVisitor так:
/// <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>();
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;
}
// Save the image to our temp directory and return a "data:" url suitable for
// the browser control to load.
string GetDataImageSrc (MimePart image)
{
using (var output = new MemoryStream()) {
image.ContentObject.DecodeTo (output);
return string.Format ("data:{0};base64,{1}", image.ContentType.MimeType, Convert.ToBase64String (output.GetBuffer(), 0, (int) output.Length));
}
}
// Replaces <img src=...> urls that refer to images embedded within the message with
// "data:" 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 = GetDataImageSrc (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);
}
}
Способ использовать этот HtmlPreviewVisitor
будет что-то вроде этого:
void Render (MimeMessage message)
{
var visitor = new HtmlPreviewVisitor();
message.Accept (visitor);
plhMessage.Controls.Add (new LiteralControl (visitor.HtmlBody));
}
Примечание: если HTML в сообщениях вы планируете оказывать все ссылки веб-адреса для своих изображений, вы можете уйти с просто изображая строку message.HtmlBody
. Однако решение HtmlPreviewVisitor
, , будет работать, даже если изображения встроены в само сообщение.
Это, вероятно, поможет вам: http://stackoverflow.com/questions/10601913/openpop-net-get-actual-message-text – ManoDestra
Ответ на этот вопрос может быть использован для получения обычного текста или HTML тела сообщений, но если изображения встроены в сообщение, то рендеринг возвращенного html не будет включать изображения (у него будут только сломанные изображения). – jstedfast