ASP.NETで、Response.TransmitFileやResponse.Writeを使ってExcel形式のデータやファイルを返送しようとしたのですが、ファイル名が化けてしまうという現象が。データの内容は問題ないのですが、ファイル保存ダイアログなどのファイル名が化けてしまいます(Chromeでは問題なし。Internet Explorer 11では化けました)。
原因
ここに書いてあるのが原因っぽいですね。
ファイル保存ダイアログがファイル名をShift_JISとして取り扱おうとする一方で、ASP.NETが返す文字列はUTF-8。結果、UTF-8の文字列をShift_JIS表示しようとして文字化けというところでしょうか。
ちなみに、書いてあった方法のうちHeaderEncodingを指定する方法は効果がありませんでした(Internet Explorer 11)。というわけで、UrlEncodeをファイル名に掛ける方法をやってみます。
コードサンプル
実際のコードはこんな感じでした。
ダメだったパターンはこれ。
Response.Buffer = true; Response.ContentType = "application/octet-stream"; Response.ContentEncoding = Encoding.GetEncoding("shift_jis"); Response.AddHeader("content-disposition", "attachment; filename=サンプル.xls")); Response.TransmitFile("./サンプル.xls");
大丈夫だったのはこれ。AddHeaderのところ、filenameに「HttpUtility.UrlEncode」をいれて、URLエンコード(%とかが混じった文字列にするやりかた)を掛けています。
Response.Buffer = true; Response.ContentType = "application/octet-stream"; Response.ContentEncoding = Encoding.GetEncoding("shift_jis"); Response.AddHeader("content-disposition", "attachment; filename=" + HttpUtility.UrlEncode("サンプル.xls")); Response.TransmitFile("./サンプル.xls");
それからこれもOKでした。
Response.Buffer = true; Response.ContentType = "application/octet-stream"; Response.ContentEncoding = Encoding.GetEncoding("shift_jis"); Response.AddHeader("content-disposition", "attachment; filename*=utf-8''" + HttpUtility.UrlEncode("サンプル.xls")); Response.TransmitFile("./サンプル.xls");
「filename*=utf-8」?
前述のコードのうち末尾のものは、AddHeaderの「filename」に続く部分が「*=utf-8''」になっています。これなんですが、多分、RFC5987の3.2.1 Definitionのところに記載されている「ext-parameter」の書式ですね。
- RFC5987 / Character Set and Language Encoding for Hypertext Transfer Protocol (HTTP) Header Field Parameters
- http://tools.ietf.org/html/rfc5987
そのあとの説明も読んでみると多分ですが、「この後に続く文字列は、この文字コードとして解釈してね。」という意味のようです(違うかも?…MIMEとか文字コードとか苦手で、英語以前のところがよくわかってなくてダメです……)。
先のコードの2番目、この指定が無くても文字化けしなかったのは、特に指定しなければUTF-8として解釈されるということでしょうか。
Server.UrlEncodeとHttpUtility.UrlEncode
サンプルにあったEncodeはServer.UrlEncodeを使って行われていたのですが、これではダメでした(やはり化ける)。HttpUtility.UrlEncodeに書き換えると化けませんでした。なんでだろう?
参考: KB 436616 ファイルをダウンロードする ASP.NET Web ページで日本語ファイル名が文字化けする
元のページが既に無く、マイクロソフトでも代替ページとなっていたので、念のため代替ページの概要を転記しておきます(いずれ消えるような気がするので)。
現象
Windows Internet Explorer 7 または Microsoft Internet Explorer 6.0 にて Content-Disposion ヘッダーの filename を HttpUtility.UrlEncode で URL エンコードしている ASPX ページからファイルをダウンロードするため、[開く] をクリックしてファイルを開いた場合、アプリケーション ウィンドウのタイトル バーに意図しないファイル名が表示されます。
原因
Content-Disposion ヘッダーの filename が エンコード方法指定なしの HttpUtility.UrlEncode で URL エンコードされている場合、Internet Explorer はそのファイル名を UTF-8 でエンコードし、キャッシュを作成します。[ダウンロード] ダイアログ ボックスの [開く] ボタンをクリックしてファイルを開く場合、キャッシュ ファイル名をデコードせずにタイトル バーに表示するため、この問題が発生します。
解決方法
Web サーバーの管理者がこの問題を解決するには、次のいずれかの方法で操作します。
問題の再現手順
- 後述の 3 つのファイル (default.html、test.aspx、test.txt) を作成します。
- 手順 1 で作成した 3 つのファイルを IIS の同じディレクトリに配置します。
- Internet Explorer を起動し、手順 2 で公開した Web ページ default.html にアクセスします。
- クエリ送信ボタンをクリックします。
- ファイルの [ダウンロード] ダイアログ ボックスが表示されたら、[開く] ボタンをクリックします。
<--- default.html ---> <html> <body> <form method="post" action="test.aspx"> <input type="submit"> </form> </body> </html>
<--- test.aspx ---> <%@ Page language="C#" %> <%@ Import Namespace="System.IO" %> <% FileStream MyFileStream = new FileStream(Server.MapPath("./test.txt"), FileMode.Open, FileAccess.Read); long FileSize = MyFileStream.Length; byte[] Buffer = new byte[(int)FileSize]; MyFileStream.Read(Buffer, 0, (int)FileSize); MyFileStream.Close(); Response.Clear(); Response.ClearHeaders(); Response.ClearContent(); Response.AddHeader("Content-Length", Buffer.ToString()); Response.AddHeader("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode("あいうえお.txt")); Response.BinaryWrite(Buffer); Response.Flush(); Response.End(); %>
<--- test.txt ---> TEST