misc.log

日常茶飯事とお仕事と

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

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

原因

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

support.microsoft.com

ファイル保存ダイアログがファイル名を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 サーバーの管理者がこの問題を解決するには、次のいずれかの方法で操作します。

  • ファイル名を 1 バイト文字に変更します。
  • ファイル名を HttpUtility.UrlEncode でエンコードせず、Web サーバー側 (ASP.NET 2.0) の構成ファイルで globalization 要素の responseHeaderEncoding 属性に "shift_jis" を指定します。
問題の再現手順
  1. 後述の 3 つのファイル (default.html、test.aspx、test.txt) を作成します。
  2. 手順 1 で作成した 3 つのファイルを IIS の同じディレクトリに配置します。
  3. Internet Explorer を起動し、手順 2 で公開した Web ページ default.html にアクセスします。
  4. クエリ送信ボタンをクリックします。
  5. ファイルの [ダウンロード] ダイアログ ボックスが表示されたら、[開く] ボタンをクリックします。
<--- 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