在 asp.net 項目中,我們可以很方便地使用 Response.WriteFile() 方法向客戶端輸出一個文件。
實際使用 asp.net
向客戶端輸出文件流時,卻出現了異常:
1、空格問題,當原文件的文件名中含有空格時,將引發客戶端獲取到的文件名與服務器端不一致。Spaces cannot
be supported by some browsers
2、中文字符亂碼,准確的是非 ASCII 字符亂碼,當原文件的文件名中含有非 ASCII
字符時,將引發客戶端獲取到的文件名錯亂。Non-US-ASCII characters cause incorrect
result
3、一些特殊字符不能被正常輸出(當然這里我並不是那些不常見的符號)
問題現象:
當原文件名包含空格時,默認將被改成下划線,即“_”;如果我們在輸出文件時對文件名使用
UrlEncode() 對其進行編碼,空格將變成加號,即“+”。
當原文件名包含中文或其他非英文字符時,由於編碼的錯誤,默認情況很糟糕,竟然完全是無法辨識的亂碼;如果我們在輸出文件時對文件名進行 UrlEncode()
對其進行編碼,這些中文將能正確地被顯示;
但注意,問題並沒有完。在Opera 或 Firefox 中,不需要經過 UrlEncode()
即能正確地顯示了;不幸地是,如果經過了 UrlEncode(),它們將無法正確地解析。
問題的解決
我們可以總結如下規律:
Internet Explorer
能在客戶端已經UrlEncode() 的字符,包括空格在內;而 Opera 等其他瀏覽器可以解析未經 UrlEncode()
的直接輸出的字符(這意味着,對於使用Opera或其他客戶端的客戶,我們不應該對它進行
UrlEncode()編碼)
為了正確地編碼,我參考一位外國人士的代碼,使用了並改進了16進制編碼方法。
參考下面的代碼,可以大部分的解決問題。由於
Firefox 不支持空格,所以以下代碼對於 Firefox ,仍不能完成具有空格的文件名的正確輸出:
在輸出文件地地方使用的代碼:
if (Request.UserAgent.Contains("MSIE") || Request.UserAgent.Contains("msie")) { // 如果客戶端使用 Microsoft Internet Explorer,則需要編碼 fileName = ToHexString(fileName); }
應該置於上述代碼同一文件或可訪問的其他類的幾個函數:
/// <summary> /// 為字符串中的非英文字符編碼 /// </summary> /// <param name="s"></param> /// <returns></returns> public static string ToHexString(string s) { char[] chars = s.ToCharArray(); StringBuilder builder = new StringBuilder(); for (int index = 0; index < chars.Length; index++) { bool needToEncode = NeedToEncode(chars[index]); if (needToEncode) { string encodedString = ToHexString(chars[index]); builder.Append(encodedString); } else { builder.Append(chars[index]); } } return builder.ToString(); } /// <summary> ///指定 一個字符是否應該被編碼 /// </summary> /// <param name="chr"></param> /// <returns></returns> private static bool NeedToEncode(char chr) { string reservedChars = "$-_.+!*'(),@=&"; if (chr > 127) return true; if (char.IsLetterOrDigit(chr) || reservedChars.IndexOf(chr) >= 0) return false; return true; } /// <summary> /// 為非英文字符串編碼 /// </summary> /// <param name="chr"></param> /// <returns></returns> private static string ToHexString(char chr) { UTF8Encoding utf8 = new UTF8Encoding(); byte[] encodedBytes = utf8.GetBytes(chr.ToString()); StringBuilder builder = new StringBuilder(); for (int index = 0; index < encodedBytes.Length; index++) { builder.AppendFormat("%{0}", Convert.ToString(encodedBytes[index], 16)); } return builder.ToString(); }
此外,針對一些瀏覽器做了一些特殊的處理,已經體現在本文示例代碼的注釋中。此代碼已經能非常完好地解決問題了,在 Internet Explorer
、Opera、Firefox 及 Chrome 中得到的體驗一致,支持中文,支持空格的正常輸出。