読者です 読者をやめる 読者になる 読者になる

misc.log

日常茶飯事とお仕事と

Response.TransmitFile等で返したExcelファイルのファイル名が化ける

ASP.NETで、Response.TransmitFileやResponse.Writeを使ってExcel形式のデータやファイルを返送しようとしたのですが、ファイル名が化けてしまうという現象が。データの内容は問題ないのですが、ファイル保存ダイアログなどのファイル名が化けてしまいます(Chromeでは問題なし。Internet Explorer 11では化けました)。

原因

ここに書いてあるのが原因っぽいですね。

ファイルをダウンロードする ASP.NET ページで日本語ファイル名が文字化けする/Microsoft Support
https://support.microsoft.com/en-us/kb/436616/ja

ファイル保存ダイアログがファイル名を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に書き換えると化けませんでした。なんでだろう?

RFCの読み方―インターネット技術の公式仕様書

RFCの読み方―インターネット技術の公式仕様書