程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 微軟的坑,你有踩過嗎?,微軟有踩

微軟的坑,你有踩過嗎?,微軟有踩

編輯:C#入門知識

微軟的坑,你有踩過嗎?,微軟有踩


  最近在做一個很簡單的小工具,需求大概是這樣的,做一個定時任務系統,定期從服務器上統計相關的業務數據,生成相關報表,然後以Excel方式發送給相關人員。任務很簡單,定時任務沒問題,生成報表沒問題,導出Excel沒問題,導出的Excel放在C盤temp目錄下,然後設好發送時間,一切都感覺妥妥的,結果也很美好,過了10秒鐘(本也測試,時間設得很短),郵件也妥妥的收到,附件中的excel表格也妥妥的能打開,報表也沒問題。Oh,yeah,so,easy..,即然結果這麼美好,那就打包發布吧。

  此處省略100字,就部署完了。

  嘀哒。。嘀哒。。,10秒,20秒,30秒,。。。沒搞錯,為了先報一個起來,我在正式環境也是設置了10秒運行一次呀?這10秒也太漫長了吧?1分鐘了,好吧,應該是出問題了,心裡仍在想著是不是哪部署出了問題呀,也不應該呀,就這麼簡單的一個小工具是吧?

  此處省略200字,檢查部署沒有任何問題。好吧,查日志。

  果不其然,報錯了,錯得還很趾高氣揚:  

System.FormatException: An invalid character was found in the mail header: '周'.
at System.Net.Mime.MailBnfHelper.GetTokenOrQuotedString(String data, StringBuilder builder)
at System.Net.Mime.ContentDisposition.ToString()
at System.Net.Mime.ContentDisposition.PersistIfNeeded(HeaderCollection headers, Boolean forcePersist)
at System.Net.Mime.MimePart.Send(BaseWriter writer)
at System.Net.Mime.MimeMultiPart.Send(BaseWriter writer)
at System.Net.Mail.SmtpClient.Send(MailMessage message)
--- End of inner exception stack trace ---
at System.Net.Mail.SmtpClient.Send(MailMessage message)

  沒搞錯,An invalid character was found in the mail header“周”? 這應該是亂碼時,才會這樣提示的吧?頁面我郵件發送直接是使用SmtpClient,根本就沒有設置header呀?

  此處省略1000字,各種度娘,各種google,各種惡補郵件頭,MIME的知識,各種設置附件ContentType。結果都一樣,唯一不一樣的就是亂碼的字,從“周“變成不是”周“。。

  此處省略2000字,突然聽同事說有可能和CurrentCulture有關系,頓時眼前一亮,趕緊google一下,果然,有答案了。

  .net 4.0中有一個Bug:Culture Bug in ContentDisposition,即只要設置了ContentDisposition的時間,任何時間,如CreationDate,或是ModificationDate,.net內部,會將這些時間通過調用DateTime的toString方法傳遞給SmtpDateTime,問題就出現在,這些時間格式是和本地語言環境相關的,toString很可能出問題。(官方說法是在vs2013中修復了這個問題)好了,找到問題就好辦了,即然和Culture有關,我又不想升級.net Framework,那就讓它和CurrentCulture暫時沒關系吧。

 1  var currentCulture = Thread.CurrentThread.CurrentCulture;
 2             var attachment = new Attachment(attachmentFile, displayName);
 3             try
 4             {
 5                 ...
 6                 Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;              
 7                 attachment.ContentDisposition.CreationDate = File.GetCreationTime(attachmentFilePath);
 8                 attachment.ContentDisposition.ModificationDate = File.GetLastWriteTime(attachmentFilePath);
 9                 attachment.ContentDisposition.ReadDate = File.GetLastAccessTime(attachmentFilePath);
10                 ...
11             }
12             finally
13             {
14                 Thread.CurrentThread.CurrentCulture = currentCulture;
15             }

  果然,那個另人討厭的”周“不見了,10秒後郵件到了,哈哈,,終於可以收工了。等等。。,為什麼我一個好好的Excel附件,發出來後,即沒文件名(實際應該有,只是亂碼了,沒顯示出來),裡面的內容也是亂碼的?天呀,你在和我開玩笑嘛?我本地開發環境都是好好的呀!!

  此處省略3000字,各種嘗試,各種用網友的辦法,各種失去理智的嘗試,各種。。無果而終。

  就快要放棄的時間,突然想起,有問題找stackoverflow.com呀,度啥娘呀。好吧,我是亂了方寸了。誰讓我本應該2小時前就下班的,到現在問題還沒頭緒呢。果然,這個問題早在n年前就有人發現了,仍然是我們可愛的微軟埋下的坑:

  

  (機器翻譯,將就著看哈)。

  好吧,我敗了。只能怪我愛得太深了,竟不曾懷疑過你。

  可我還是不想升級Framework,生產環境,可不是鬧著玩的呢。 

  試著用網友ZYD的方法,把文件名先編碼拆分,問題可以得到解決:

  

1 ... 2 Attachment attachment = null; 3 //添加附件 4 if (!String.IsNullOrEmpty(attachmentFilePath) && File.Exists(attachmentFilePath)) 5 { 6 attachment = CreateAttachment(WriteFileToMemory(attachmentFilePath), attachmentFileName,contentType,attachmentFilePath); 7 message.Attachments.Add(attachment); 8 9 } 10 ... 11 12 public static Attachment CreateAttachment(Stream attachmentFile, string displayName, string contentType, string attachmentFilePath) 13 { 14 var currentCulture = Thread.CurrentThread.CurrentCulture;//.net4.0 bug, 15 var attachment = new Attachment(attachmentFile, displayName); 16 try 17 { 18 Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; 19 attachment.ContentType = new ContentType(contentType); 20 attachment.ContentDisposition.CreationDate = File.GetCreationTime(attachmentFilePath); 21 attachment.ContentDisposition.ModificationDate = File.GetLastWriteTime(attachmentFilePath); 22 attachment.ContentDisposition.ReadDate = File.GetLastAccessTime(attachmentFilePath); 23 attachment.TransferEncoding = TransferEncoding.Base64; 24 attachment.NameEncoding = Encoding.UTF8; 25 string encodedAttachmentName = Convert.ToBase64String(Encoding.UTF8.GetBytes(displayName)); 26 encodedAttachmentName = SplitEncodedAttachmentName(encodedAttachmentName); 27 attachment.Name = encodedAttachmentName; 28 } 29 finally 30 { 31 Thread.CurrentThread.CurrentCulture = currentCulture; 32 } 33 return attachment; 34 } 35 36 private static Stream WriteFileToMemory(string filePath) 37 { 38 var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); 39 return fileStream; 40 } 41 42 private static string SplitEncodedAttachmentName(string encoded) 43 { 44 const string encodingtoken = "=?UTF-8?B?"; 45 const string softbreak = "?="; 46 const int maxChunkLength = 30; 47 int splitLength = maxChunkLength - encodingtoken.Length - (softbreak.Length * 2); 48 IEnumerable<string> parts = SplitByLength(encoded, splitLength); 49 string encodedAttachmentName = encodingtoken; 50 foreach (var part in parts) 51 { 52 encodedAttachmentName += part + softbreak + encodingtoken; 53 } 54 encodedAttachmentName = encodedAttachmentName.Remove(encodedAttachmentName.Length - encodingtoken.Length, encodingtoken.Length); 55 return encodedAttachmentName; 56 } 57 58 private static IEnumerable<string> SplitByLength(string stringToSplit, int length) 59 { 60 while (stringToSplit.Length > length) 61 { 62 yield return stringToSplit.Substring(0, length); 63 stringToSplit = stringToSplit.Substring(length); 64 } 65 if (stringToSplit.Length > 0) 66 { 67 yield return stringToSplit; 68 } 69 } View Code

  過程是坎坷的,結局是美好的!

  只能怪我愛得太深了,竟不曾懷疑過你。用它來結語,再好不過了吧!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved