C#中使用IMAP协议读取一些附件的问题
Problems with reading some attachments using IMAP protocol in C #
我想请教您以下问题:
我有两个代码可以阅读带有各自附件的电子邮件:
第一个,我用S22.IMAP dll:
using (ImapClient Client = new ImapClient(imap, 993, usuario, psw, AuthMethod.Login, true))
{
IEnumerable<uint> uids = Client.Search(SearchCondition.Unseen());//Correos no leídos
IEnumerable<MailMessage> messages = Client.GetMessages(uids, FetchOptions.Normal);
conexion.stringconeccion = stringconeccion;
conexion.conectar();
String ts = "start transaction";
MySqlCommand datos_ts = new MySqlCommand(ts, conexion.con);
datos_ts.ExecuteScalar();
DataTable dt_existeXML = new DataTable();
int insercion = 0;
foreach (MailMessage msg in messages)
{
foreach (Attachment atc in msg.Attachments)
{
if (System.IO.Path.GetExtension(msg.Attachments[0].Name) == ".xml")
{
String archivoXML_texto = "";
byte[] allBytes = new byte[msg.Attachments[0].ContentStream.Length];
int bytesRead = msg.Attachments[0].ContentStream.Read(allBytes, 0, (int)msg.Attachments[0].ContentStream.Length);
using (MemoryStream memory = new MemoryStream(allBytes))
{
StreamReader archivoXML = new StreamReader(memory);
archivoXML_texto = archivoXML.ReadToEnd();
archivoXML.Close();
memory.Dispose();
}
}
}
}
第二个代码,使用 MailKit DLL:
using (var client = new ImapClient ()) {
client.Connect ("imap.gmail.com", 993, SecureSocketOptions.SslOnConnect);
client.Authenticate ("correo@gmail.com", "clave");
client.Inbox.Open (FolderAccess.ReadOnly);
var uids = client.Inbox.Search(SearchQuery.NotSeen);
foreach (var uid in uids)
{
var message = client.Inbox.GetMessage(uid);
foreach (var attachment in message.Attachments.OfType<MimePart>())
{
byte[] allBytes = new byte[attachment.Content.Stream.Length];
int bytesRead = attachment.Content.Stream.Read(allBytes, 0, (int)attachment.Content.Stream.Length);
string texto_definitivo = "";
String archivoXML_textoBase64 = "";
using (MemoryStream memory = new MemoryStream(allBytes))
{
StreamReader archivoXML = new StreamReader(memory);
archivoXML_textoBase64 = archivoXML.ReadToEnd();
byte[] temp_backToBytes = Convert.FromBase64String(archivoXML_textoBase64);
texto_definitivo = Encoding.ASCII.GetString(temp_backToBytes);
archivoXML.Close();
memory.Dispose();
}
}
}
client.Disconnect (true);
}
但我意识到有些附件没有阅读,而且,我注意到一些未阅读的附件的共同点,没有附件图标。但是当我打开邮件时,我看到它们有附件:
enter image description here
例如标红的图片,右侧的附件图标不显示。但是当我打开它时,我确认它有一个附加文件。只是那些文件,两个代码中的一个,不看附件。
我的问题是:
我怎么也看那些附件?
我应该配置或启用邮件中的任何选项吗?什么以及如何?
发邮件有误吗?
怎么解决的?
更新
希望我已经理解了。以下代码尝试在可能的情况下获取附件的文本/内容。如有不妥请指正
public static void DownloadBodyParts ()
{
using (var client = new ImapClient ()) {
client.Connect ("imap.gmail.com", 993, SecureSocketOptions.SslOnConnect);
client.Authenticate("correo@gmail.com", "clave");
client.Inbox.Open(FolderAccess.ReadWrite);
var uids = client.Inbox.Search(SearchQuery.NotSeen);
foreach (var uid in uids)
{
var message = client.Inbox.GetMessage(uid);
var attachments = message.BodyParts.OfType<MimePart>().Where(part => !string.IsNullOrEmpty(part.FileName));
foreach (MimePart atch in attachments)
{
using (var memory = new MemoryStream())
{
atch.Content.DecodeTo(memory);
var buffer = memory.ToArray();
var text = Encoding.UTF8.GetString(buffer);
}
}
}
client.Disconnect (true);
}
来自 MailKit FAQ:
问:如何判断邮件是否有附件?
在大多数情况下,body 的 MIME-type 或 multipart/mixed
包含多个部分的邮件可能有附件。如上所示,multipart/mixed
的第一部分通常是消息的文本 body,但并不总是那么简单。
一般来说,MIME 附件会有一个 Content-Disposition
header 的值是 attachment。要获取符合此条件的 body 部分的列表,您可以使用 MimeMessage.Attachments 属性.
遗憾的是,并非所有邮件客户端都遵循此约定,因此您可能需要编写自己的自定义逻辑。例如,您可能希望处理设置了 name
或 filename
参数的所有 body 部分:
var attachments = message.BodyParts.OfType<MimePart> ().Where (part => !string.IsNullOrEmpty (part.FileName));
一种更复杂的方法是将邮件的主要文本 body 部分未引用的 body 部分视为附件。换句话说,将任何未用于呈现消息的 body 部分视为附件。有关如何执行此操作的示例,请考虑以下代码片段:
/// <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> ();
readonly string tempDir;
string body;
/// <summary>
/// Creates a new HtmlPreviewVisitor.
/// </summary>
/// <param name="tempDirectory">A temporary directory used for storing image files.</param>
public HtmlPreviewVisitor (string tempDirectory)
{
tempDir = tempDirectory;
}
/// <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 "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.Content.DecodeTo (output);
}
return "file://" + path.Replace ('\', '/');
}
// 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 = 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 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);
}
}
And the way you'd use this visitor might look something like this:
void Render (MimeMessage message)
{
var tmpDir = Path.Combine (Path.GetTempPath (), message.MessageId);
var visitor = new HtmlPreviewVisitor (tmpDir);
Directory.CreateDirectory (tmpDir);
message.Accept (visitor);
DisplayHtml (visitor.HtmlBody);
DisplayAttachments (visitor.Attachments);
}
使用上述技术呈现邮件后,您将获得未使用的附件列表,即使它们不符合 MimeMessage.Attachments
[=60 使用的简单标准=].
更新:
下面是如何获取 MimePart
的文本内容(假设 MimePart
不能转换为 TextPart
,这使得这个过程非常简单)。
using (var memory = new MemoryStream ()) {
mimePart.Content.DecodeTo (memory);
var buffer = memory.ToArray ();
var text = Encoding.UTF8.GetString (buffer);
}
当然,如果MimePart
可以转换成TextPart
,那就更简单了:
var textPart = (TextPart) mimePart;
var text = textPart.Text;
我想请教您以下问题:
我有两个代码可以阅读带有各自附件的电子邮件:
第一个,我用S22.IMAP dll:
using (ImapClient Client = new ImapClient(imap, 993, usuario, psw, AuthMethod.Login, true))
{
IEnumerable<uint> uids = Client.Search(SearchCondition.Unseen());//Correos no leídos
IEnumerable<MailMessage> messages = Client.GetMessages(uids, FetchOptions.Normal);
conexion.stringconeccion = stringconeccion;
conexion.conectar();
String ts = "start transaction";
MySqlCommand datos_ts = new MySqlCommand(ts, conexion.con);
datos_ts.ExecuteScalar();
DataTable dt_existeXML = new DataTable();
int insercion = 0;
foreach (MailMessage msg in messages)
{
foreach (Attachment atc in msg.Attachments)
{
if (System.IO.Path.GetExtension(msg.Attachments[0].Name) == ".xml")
{
String archivoXML_texto = "";
byte[] allBytes = new byte[msg.Attachments[0].ContentStream.Length];
int bytesRead = msg.Attachments[0].ContentStream.Read(allBytes, 0, (int)msg.Attachments[0].ContentStream.Length);
using (MemoryStream memory = new MemoryStream(allBytes))
{
StreamReader archivoXML = new StreamReader(memory);
archivoXML_texto = archivoXML.ReadToEnd();
archivoXML.Close();
memory.Dispose();
}
}
}
}
第二个代码,使用 MailKit DLL:
using (var client = new ImapClient ()) {
client.Connect ("imap.gmail.com", 993, SecureSocketOptions.SslOnConnect);
client.Authenticate ("correo@gmail.com", "clave");
client.Inbox.Open (FolderAccess.ReadOnly);
var uids = client.Inbox.Search(SearchQuery.NotSeen);
foreach (var uid in uids)
{
var message = client.Inbox.GetMessage(uid);
foreach (var attachment in message.Attachments.OfType<MimePart>())
{
byte[] allBytes = new byte[attachment.Content.Stream.Length];
int bytesRead = attachment.Content.Stream.Read(allBytes, 0, (int)attachment.Content.Stream.Length);
string texto_definitivo = "";
String archivoXML_textoBase64 = "";
using (MemoryStream memory = new MemoryStream(allBytes))
{
StreamReader archivoXML = new StreamReader(memory);
archivoXML_textoBase64 = archivoXML.ReadToEnd();
byte[] temp_backToBytes = Convert.FromBase64String(archivoXML_textoBase64);
texto_definitivo = Encoding.ASCII.GetString(temp_backToBytes);
archivoXML.Close();
memory.Dispose();
}
}
}
client.Disconnect (true);
}
但我意识到有些附件没有阅读,而且,我注意到一些未阅读的附件的共同点,没有附件图标。但是当我打开邮件时,我看到它们有附件:
enter image description here
例如标红的图片,右侧的附件图标不显示。但是当我打开它时,我确认它有一个附加文件。只是那些文件,两个代码中的一个,不看附件。
我的问题是:
我怎么也看那些附件? 我应该配置或启用邮件中的任何选项吗?什么以及如何? 发邮件有误吗? 怎么解决的?
更新
希望我已经理解了。以下代码尝试在可能的情况下获取附件的文本/内容。如有不妥请指正
public static void DownloadBodyParts ()
{
using (var client = new ImapClient ()) {
client.Connect ("imap.gmail.com", 993, SecureSocketOptions.SslOnConnect);
client.Authenticate("correo@gmail.com", "clave");
client.Inbox.Open(FolderAccess.ReadWrite);
var uids = client.Inbox.Search(SearchQuery.NotSeen);
foreach (var uid in uids)
{
var message = client.Inbox.GetMessage(uid);
var attachments = message.BodyParts.OfType<MimePart>().Where(part => !string.IsNullOrEmpty(part.FileName));
foreach (MimePart atch in attachments)
{
using (var memory = new MemoryStream())
{
atch.Content.DecodeTo(memory);
var buffer = memory.ToArray();
var text = Encoding.UTF8.GetString(buffer);
}
}
}
client.Disconnect (true);
}
来自 MailKit FAQ:
问:如何判断邮件是否有附件?
在大多数情况下,body 的 MIME-type 或 multipart/mixed
包含多个部分的邮件可能有附件。如上所示,multipart/mixed
的第一部分通常是消息的文本 body,但并不总是那么简单。
一般来说,MIME 附件会有一个 Content-Disposition
header 的值是 attachment。要获取符合此条件的 body 部分的列表,您可以使用 MimeMessage.Attachments 属性.
遗憾的是,并非所有邮件客户端都遵循此约定,因此您可能需要编写自己的自定义逻辑。例如,您可能希望处理设置了 name
或 filename
参数的所有 body 部分:
var attachments = message.BodyParts.OfType<MimePart> ().Where (part => !string.IsNullOrEmpty (part.FileName));
一种更复杂的方法是将邮件的主要文本 body 部分未引用的 body 部分视为附件。换句话说,将任何未用于呈现消息的 body 部分视为附件。有关如何执行此操作的示例,请考虑以下代码片段:
/// <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> ();
readonly string tempDir;
string body;
/// <summary>
/// Creates a new HtmlPreviewVisitor.
/// </summary>
/// <param name="tempDirectory">A temporary directory used for storing image files.</param>
public HtmlPreviewVisitor (string tempDirectory)
{
tempDir = tempDirectory;
}
/// <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 "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.Content.DecodeTo (output);
}
return "file://" + path.Replace ('\', '/');
}
// 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 = 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 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);
}
}
And the way you'd use this visitor might look something like this:
void Render (MimeMessage message)
{
var tmpDir = Path.Combine (Path.GetTempPath (), message.MessageId);
var visitor = new HtmlPreviewVisitor (tmpDir);
Directory.CreateDirectory (tmpDir);
message.Accept (visitor);
DisplayHtml (visitor.HtmlBody);
DisplayAttachments (visitor.Attachments);
}
使用上述技术呈现邮件后,您将获得未使用的附件列表,即使它们不符合 MimeMessage.Attachments
[=60 使用的简单标准=].
更新:
下面是如何获取 MimePart
的文本内容(假设 MimePart
不能转换为 TextPart
,这使得这个过程非常简单)。
using (var memory = new MemoryStream ()) {
mimePart.Content.DecodeTo (memory);
var buffer = memory.ToArray ();
var text = Encoding.UTF8.GetString (buffer);
}
当然,如果MimePart
可以转换成TextPart
,那就更简单了:
var textPart = (TextPart) mimePart;
var text = textPart.Text;