一、故事
首先通过CDO.Message来获取邮件EML相关数据:邮件标题、邮件内容、邮件附件、发件人、收件人、CC主要就这么几个,其次通过MailMessage来组织邮件通过Python来发送邮件!
就这么简简单单的需求!!问题出现了,中文附件名!Web打开始终是乱码!使用邮件客户端FireFox是OK的,查看了FireFox源码发现是乱码,这点说明FireFox的强大,非常强大!
Content-Type: application/octet-stream; name=鎶ラ攢鍗昪s.xlsx Content-Transfer-Encoding: base64 Content-Disposition: attachment
见图见图
……
二、折腾中
出了问题想办法,一定要抱着始终相信一定可以解决的尤其是搞IT的一定有方法!大事化小,小事化无。先找卧底!第一个想到的便是CDO.Message那就从他下手。最后发现他是平民!
先说说走的路,干货的路,其他摸索的方法想了下数数应该有4,5种了:
读取EML转换成流,再获取附件再解码,发现中文名OK
先看结果
再看看代码
public class AttachmentExtractor { private static int imageCount; public static void Method(string path) { StreamReader reader = null; try { reader = new StreamReader(path); string line; StringBuilder sb = new StringBuilder(); while ((line = reader.ReadLine()) != null) { sb.Append(line.ToLower()); if (line.ToLower().StartsWith("content-disposition:attachment;") || line.ToLower().StartsWith("content-disposition: attachment;")) // found attachment { string fileName = GetAttachment(reader, line); fileName = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(fileName.Replace("=?utf-8?B?", "").Replace("?=", ""))); } if (line.ToLower().StartsWith("content-type:image/")) // found embedded image { ExtractContent(reader, GetImage(reader, line)); } } } catch (IOException) { Console.WriteLine("找不到文件!"); } finally { if (reader != null) reader.Close(); } } private static string GetAttachment(TextReader reader, string line) { if (!line.Contains("filename")) { line = reader.ReadLine(); // Thunderbird: filename start at //second line } return GetFilenameNew(reader, line); } private static string GetImage(TextReader reader, string line) { if (!line.Contains("name")) { line = reader.ReadLine(); // Thunderbird: filename start at //second line } if (!line.Contains("name")) // embedded image does not have name { AdvanceToEmptyLine(reader); return "image" + imageCount++ + ".jpg"; // default to jpeg } return GetFilename(reader, line); } private static string GetFilename(TextReader reader, string line) { string filename; int filenameStart = line.IndexOf('"') + 1; if (filenameStart > 0) { filename = line.Substring(filenameStart, line.Length - filenameStart - 1); } else // filename does not have quote { filenameStart = line.IndexOf('=') + 1; filename = line.Substring(filenameStart, line.Length - filenameStart); } AdvanceToEmptyLine(reader); return filename; } private static string GetFilenameNew(TextReader reader, string line) { string filename; int filenameStart = line.IndexOf('"') + 1; if (filenameStart > 0) { filename = line.Substring(filenameStart, line.Length - filenameStart - 1); } else // filename does not have quote { filenameStart = line.IndexOf('=') + 1; filename = line.Substring(filenameStart, line.Length - filenameStart); } return filename; } private static void AdvanceToEmptyLine(TextReader reader) { string line; while ((line = reader.ReadLine()) != null) { if (String.IsNullOrEmpty(line)) break; } } private static void ExtractContent(TextReader reader, string filename) { string line; var content = new StringBuilder(); while ((line = reader.ReadLine()) != null) { if (String.IsNullOrEmpty(line)) break; content.Append(line); } if (content.Length > 0) { byte[] buffer = Convert.FromBase64String(content.ToString()); #region 7.7 if (!File.Exists(filename)) { return; } #endregion using (Stream writer = new FileStream(filename, FileMode.Create)) { writer.Write(buffer, 0, buffer.Length); } } } }
public RedEmail() { InitializeComponent(); this.txtEmailPath.Text = "C:\Users\Administrator\Desktop\4a3266e6-23bd-11e5-9703-0050569a7cc2.eml"; AttachmentExtractor.Method(txtEmailPath.Text); }
仔细看完代码会发现获取的附件名是编码过的,需要截取。这个要注意!发现经常不写写,不说说都不知道如何表达我那被困的感受!!!不过!有结果就是胜利!如下:
可喜的是,我找到了原因:CDO.Message不是卧底!是个良民!!!他只是一个善良的二道贩子!
三、看到曙光
好了,总共就两人,一个平民了,那么另一个一定是卧底咯-MailMessage
先看胜利的结果,这个喜悦之情那!!
一个是EML里面的附件-乱码,一个是通过改良后代码上传上去的-OK……(ps 写博客都不忘给我老婆店铺做广告,主要是因为我们博客园老牛B了,经常会被其他网站转载,又不写转载信息!)
搞IT的代码最直接看看代码,如下:
//MemoryStream ms = // new MemoryStream(File.ReadAllBytes(@"C:\Users\Administrator\Desktop\RDP_需求规格说明书.docx")); ////message.Attachments.Add(new System.Net.Mail.Attachment(ms, "RDP_需求规格说明书.docx")); //System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Text.Plain); //System.Net.Mail.Attachment attach = new System.Net.Mail.Attachment(ms, ct); //attach.ContentDisposition.FileName = "产品经理2.docx"; //attach.NameEncoding = Encoding.GetEncoding("utf-8"); // `message.Attachments.Add(attach); System.Net.Mail.Attachment attachment = AttachmentHelper.CreateAttachment(@"C:\Users\Administrator\Desktop\产品经理2.jpg", "产品经理2.jpg", TransferEncoding.Base64); message.Attachments.Add(attachment); //var attachment = new Attachment(new MemoryStream(File.ReadAllBytes(@"C:\Users\Administrator\Desktop\产品经理2.jpg")), new System.Net.Mime.ContentType("application/vnd.ms-excel")); ////bool flag = File.Exists(@"C:\Users\Administrator\Desktop\产品经理2.jpg"); //FileStream fs = new FileStream(@"C:\Users\Administrator\Desktop\产品经理2.jpg", FileMode.Open, FileAccess.Read); //System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Text.Plain); //System.Net.Mail.Attachment attach = new System.Net.Mail.Attachment(fs, ct); //attach.ContentDisposition.FileName = "产品经理2.jpg"; //fs.Close(); //message.Attachments.Add(attach);
看到了么!注释的就是实验的!我说我是折腾了半天解决的!
好了,揭开神秘的面纱AttachmentHelper
public class AttachmentHelper { public static System.Net.Mail.Attachment CreateAttachment(string attachmentFile, string displayName, TransferEncoding transferEncoding) { System.Net.Mail.Attachment attachment = new System.Net.Mail.Attachment(attachmentFile); attachment.TransferEncoding = transferEncoding; string tranferEncodingMarker = String.Empty; string encodingMarker = String.Empty; int maxChunkLength = 0; switch (transferEncoding) { case TransferEncoding.Base64: tranferEncodingMarker = "B"; encodingMarker = "UTF-8"; maxChunkLength = 30; break; case TransferEncoding.QuotedPrintable: tranferEncodingMarker = "Q"; encodingMarker = "ISO-8859-1"; maxChunkLength = 76; break; default: throw (new ArgumentException(String.Format("The specified TransferEncoding is not supported: {0}", transferEncoding, "transferEncoding"))); } attachment.NameEncoding = Encoding.GetEncoding(encodingMarker); string encodingtoken = String.Format("=?{0}?{1}?", encodingMarker, tranferEncodingMarker); string softbreak = "?="; string encodedAttachmentName = encodingtoken; if (attachment.TransferEncoding == TransferEncoding.QuotedPrintable) encodedAttachmentName = HttpUtility.UrlEncode(displayName, Encoding.Default).Replace("+", " ").Replace("%", "="); else encodedAttachmentName = System.Convert.ToBase64String(Encoding.UTF8.GetBytes(displayName)); encodedAttachmentName = SplitEncodedAttachmentName(encodingtoken, softbreak, maxChunkLength, encodedAttachmentName); attachment.Name = encodedAttachmentName; return attachment; } private static string SplitEncodedAttachmentName(string encodingtoken, string softbreak, int maxChunkLength, string encoded) { int splitLength = maxChunkLength - encodingtoken.Length - (softbreak.Length * 2); var parts = SplitByLength(encoded, splitLength); string encodedAttachmentName = encodingtoken; foreach (var part in parts) encodedAttachmentName += part + softbreak + encodingtoken; encodedAttachmentName = encodedAttachmentName.Remove(encodedAttachmentName.Length - encodingtoken.Length, encodingtoken.Length); return encodedAttachmentName; } private static IEnumerable<string> SplitByLength(string stringToSplit, int length) { while (stringToSplit.Length > length) { yield return stringToSplit.Substring(0, length); stringToSplit = stringToSplit.Substring(length); } if (stringToSplit.Length > 0) yield return stringToSplit; } }
这个牛B的类不是我写的!声明下!我可没那么牛×,是哥千辛万苦+输入了英文才找到的!更坚定了我要学好英文的夙愿!!!!
四、后话
好了,可以安心改改代码,发布一个Demo了。
总结下:
1、要有不放弃不抛弃。
2、要敢自我调侃的娱乐精神。
3、关键时刻别忘了Google,国外的月亮有时候真的比国内圆!
一口气,好了,我去WC 憋死我了………………