Struts2で日本語ファイル名のファイルダウンロードがえっらいハマったのでメモです。
#streamのparamについて
単純にファイルをダウンロードするだけであれば、streamのパラメタを以下のようにすればダウンロードできます。
@Result(name = "success", type = "stream",
params = { "inputName", "inputStream",
"contentType", "application/octet-stream; charset=UTF-8",
"contentLength", "${ contentLength }",
"contentDisposition", "attachment; filename = ${fileName}"
}
)
fileName
にファイル名を指定してやればいいのですが、2バイト文字が含まれるとブラウザ側で化けます。
これを回避するにはブラウザを判定して適切にファイル名をエンコードする必要がありますが、このfileName
だけでは足りませんので、パラメタを以下のようにします。
@Result(name = "success", type = "stream",
params = { "inputName", "inputStream",
"contentType", "application/octet-stream; charset=UTF-8",
"contentLength", "${ contentLength }",
"contentDisposition", "attachment; filename = ${fileName};filename*=utf-8''${encodedFileName}"
}
)
この追加したencodedFileName
にもエンコードしたファイル名を指定しますが、こちらはブラウザに関係なくUTF8でエンコードしたものを指定します。
つまり、 ブラウザごとにエンコードしたファイル名とUTF8でエンコードしたファイル名 の2つが必要になります。
この例ではfileName
に指定するファイル名をブラウザごとにエンコードしてやる必要があります。
#サンプル
以下、ファイルダウンロードアクションのサンプルです。
@Result(name = "success", type = "stream", params = { "inputName",
"inputStream", "contentType",
"application/octet-stream; charset=UTF-8", "contentLength",
"${ contentLength }", "contentDisposition",
"attachment; filename = ${fileName};filename*=utf-8''${encodedFileName}" })
@Action(value = "download")
public class DownloadAction extends ActionSupport {
/** ファイル名. */
private String fileName;
/** エンコードしたファイル名. */
private String encodedFileName;
/** ファイルサイズ. */
private long contentLength;
/** ストリーム. */
private InputStream inputStream;
// getter/setterのコードは省略
@Override
public final String execute() throws Exception {
// ダウンロード対象ファイル
String filePath = "日本語ファイル.txt";
// 対象ファイル読み込み
File exportFile = new File(filePath);
// ストリームに読込
this.inputStream = new BufferedInputStream(
new FileInputStream(filePath));
// ファイルサイズ取得
this.contentLength = exportFile.length();
// ブラウザで分岐
String agent = ServletActionContext.getRequest()
.getHeader("User-Agent");
if (agent.indexOf("IE ") >= 0) {
// IE
this.fileName = URLEncoder.encode(exportFile.getName(), "UTF-8");
this.encodedFileName = URLEncoder.encode(exportFile.getName(),
"UTF-8");
} else {
// IE以外
this.fileName = exportFile.getName();
this.encodedFileName = URLEncoder.encode(exportFile.getName(),
"UTF-8");
}
return "success";
}
}
この例ではIEのみ分岐させてfileName
をエンコードしています。
とりあえずIE8~11、Firefox、Chromeで2バイト文字のファイルがダウンロードできることは確認しています。
#server.xml
これ書くの忘れてた・・・
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>
を
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8" useBodyEncodingForURI="true"/>
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" URIEncoding="UTF-8" useBodyEncodingForURI="true"/>
の様にエンコードを指定してやります。
特にEclipseの開発環境をそのまま使用しているとデフォルト値なので、これ設定しないとだめだったわ。
以上!