﻿# ASP.NET 如何設定強制下載檔案並正確處理中文檔名的問題

我想一般人的作法都是透過設定 HTTP 回應 Content-Disposition 標頭(Header)的方式告知用戶端(Browser)強制下載檔案的，例如：

```
string fileName = "ExportData.csv";

string strContentDisposition = String.Format("{0}; filename=\"{1}\"", "attachment", fileName);

Response.AddHeader("Content-Disposition", strContentDisposition);
```

透過上述程式碼，就可以讓 Browser 強制下載此頁的內容，也就是該頁的內容（可能是文件或二進位檔案）不會直接在瀏覽器中開啟或下載後直接開啟相關程式（如：Office）。

其中 Content-Disposition 標頭的第一組參數是 attachment，代表此頁為一個「附件檔」，如果你將 attachment 改成 inline 的話，就代表這是一個內嵌與其他網頁內檔案（如：圖檔、CSS、JavaScript、Flash、...），而這也是「預設」的設定，所以也就等於不加上 Content-Disposition 標頭的情況。

而 Content-Disposition 標頭的第二組參數是 filename，也就是你可以指定下載檔案時預設的儲存檔名，在此範例中的下載檔名是 ExportData.csv （如下圖示）

 ![IE 強制下載檔案畫面](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/9b3bc32f090e35cba02c-Screenshot-080419-135721_3.png)

雖然這個小技巧很好用，不過當你的檔名內含「中文字」的時候，卻會發生以下錯誤：

[![設定強制下載檔案並使用中文檔名時的錯誤畫面](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/fd79c9c207ce900b9464-Screenshot-080419-140101_thumb.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/2117cccb15be3608cbb6-Screenshot-080419-140101_2.png)

經測試發現，這個問題只會再 IE 出現，當我在使用 Firefox 的時候並不會有這個問題，嚴格算起來應該算是 IE 的 Bug。

我從 MSDN 的 [HttpResponse.HeaderEncoding 屬性](http://msdn2.microsoft.com/zh-tw/library/system.web.httpresponse.headerencoding\(VS.80\).aspx) 說明文件發現 ASP.NET 在回應 HTTP 標頭的時候預設編碼是用 [System.Text.UTF8Encoding](http://msdn.microsoft.com/library/cht/default.asp?url=/library/cht/cpref/html/frlrfsystemtextutf8encodingclasstopic.asp) 類別，但問題是 IE 瀏覽器無法正確解析 UTF-8 的 HTTP Header。從網路上得到的一般性解法就是特別指定 Response.HeaderEncoding 的編碼，因為 IE 瀏覽器在繁體中文的作業系統下可以支援的編碼是 Big5，所以照理說只要設定正確的編碼就能夠正確下載中文檔名，如下程式片段：

```
Response.HeaderEncoding = Encoding.GetEncoding("big5");
```

不過經過我測試的結果，這段 Code 在 ASP.NET Development Server 中執行是「完全無效」的，所有的中文字還是以 UTF-8 編碼輸出，但是同一段程式碼在 IIS 6.0 中卻可以正常執行，雖然可以正確輸出 Big5 編碼的 HTTP Header，但是下載後的檔名竟然有幾台機器會變成亂碼，雖然大多數主機下載的檔名是正確的，但此問題依然困擾著我，因為當你設定了 Big5 編碼後，中國大陸簡體中文的用戶又無法下載了，或是下載後檔名一樣變成亂碼。

為了解決這個問題，我不斷的上網尋找資料，不過都沒有人提出有別於設定 Response.HeaderEncoding 的作法，所以就一直試一直試，試了快 4 個鐘頭，結果看到頭暈眼花，還是放棄了。但今天突然靈機一動想說將中文檔名用 [Server.UrlPathEncode](http://msdn2.microsoft.com/zh-tw/library/system.web.httpserverutility.urlpathencode.aspx) 方法編碼看看，結果真的成功了！以下是程式碼範例：

```
string fileName = Server.UrlPathEncode("匯出資料檔080419.csv");

string strContentDisposition = String.Format("{0}; filename=\"{1}\"", "attachment", fileName);

Response.AddHeader("Content-Disposition", strContentDisposition);
```

此技巧不但可以正確下載中文檔名，且也不需要設定任何 Response.HeaderEncoding 就可以正常下載，同一段程式碼同時可以給任何支援 UTF-8 的作業系統下載，包括使用簡體中文的大陸用戶也可以正確下載檔案了，真是大快人心啊。

因為我們的目的是「要讓使用者能正確下載含有中文檔名的檔案」且目的也算是達成了，不過如果使用者直接在檔案下載視窗點選「開啟舊檔(O)」的話（如下圖）：

![檔案下載](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/adbb3009b5dc4c96126c-Screenshot-080419-153729_3.png)

IE 會先將該檔案暫存於 IE 的暫存目錄裡並且直接開啟該檔案，不過檔名卻會變成 **%e5%8c%af%e5%87%ba%e8%b3%87%e6%96%99%e6%aa%94080419.csv** （編碼過的檔名），如果使用者只是想開啟來看一下不存檔的話，那到沒什麼大礙，如果使用者按下「另存新檔」要儲存檔案時，那檔名就變的亂七八糟了，唉～ 殘念！這點真的無解！

另外我也在 Firefox 瀏覽器中測試，發現另存新檔或開啟檔案的檔名也一樣會變成編碼過的檔名( **%e5%8c%af%e5%87%ba%e8%b3%87%e6%96%99%e6%aa%94080419.csv** )，所以沒辦法一招半式闖江湖，我又調整了一下程式碼，讓 Content-Disposition 標頭中的檔名可以針對使用者透過 IE 瀏覽器下載時將檔案編碼：

```
string fileName = "匯出資料檔080419.csv";
if (Request.Browser.Browser == "IE") {
    fileName = Server.UrlPathEncode(fileName);
}
string strContentDisposition = String.Format("{0}; filename=\"{1}\"", "attachment", fileName);
Response.AddHeader("Content-Disposition", strContentDisposition);
```

這個版本應該是最終版了，除了 IE 開啟舊檔的問題無解外，其他應該是完美了。

這個技巧不只可以用在 ASP.NET，同樣的原理一樣也可以用在 JSP, PHP, Perl, Ruby, Python 或其他程式語言上。

**2008/04/24 補充**

> 我原本的程式在抓檔案的 HttpHandler 中有加上「不要快取」的設定，如下：
> 
> ```
> Response.Cache.SetNoStore();
> Response.Cache.SetCacheability(HttpCacheability.NoCache);
> ```
> 
> 但是如果使用者直接在檔案下載視窗點選「開啟舊檔(O)」的話，卻會造成「部分 IE 瀏覽器」在下載的時會發生找不到檔案的狀況，如下圖示：
> 
> ![下載的時會發生找不到檔案的狀況](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/2d66e5a05d03616ae0e3-Screenshot-080424-112055_3.png)
> 
> 其實我原本的用意是不要讓下載的檔案被瀏覽器快取住，但還是不要加上好了，以免造成使用者個困擾。
