メンチカツには醤油でしょ!!

AWS/Java/Node.js/Spreadsheets/Docker/Jenkins/コミュニティ・勉強会レポを主とした技術系ブログ

Struts2でApache POIのExcelダウンロードで日本語ファイル名が文字化け

環境

Wildfly 10.0.0.Final
Struts 2.3.29
Struts Convention Plugin 2.3.29
Struts2 CDI Plugin 2.3.29
struts.xml での struts.enable.DynamicMethodInvocation は true

現象

Apache POIで生成したxlsをStruts2でダウンロードすると日本語ファイルが文字化け…
csvのダウンロードはうまくいってるので
POIが良くないのか、それとも
レスポンスヘッダに設定している
contentType:application/msexcel; charset=utf-8
あたりか…!?ということで試行錯誤開始。

処理概要

@Result(name = "download", type = "stream", params = {
"inputName", "inputStream",
"contentType", "application/msexcel; charset=${ charset }",
"contentLength", "${ contentLength }",
"contentDisposition", "attachment; filename = ${ filename };filename*=utf-8''${ encodedFilename }"
})
public class ExcelDownload extends ActionSupport {

// 中略
public void download() {
org.apache.poi.ss.usermodel.Workbook wb = ワークブック生成処理();

byte[] byteArray = null;
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
wb.write(out);
byteArray = out.toByteArray(); // wb⇒outへ書き出しbyteArrayを得る.
} catch (IOException e) {
// エラーハンドリング処理.
}

if (ServletActionContext.getRequest().getHeader("User-Agent").indexOf("IE ") >= 0) {
// IEならfilenameもエンコードする.
this.filename = URLEncoder.encode(filename, "UTF-8");
} else {
// Non IE.
this.filename = filename;
}
this.encodedFilename = URLEncoder.encode(filename, "UTF-8");

// 中略

return "download";
}
}

よくあるこんな感じのソースで、調べてて一番有力なのが下記です。
CSVだとこれでうまくいってました。

Struts2のファイルダウンロード処理で日本語名のファイルをダウンロードする - Qiita

fileNameencodedFileNameに対して
ブラウザによって(IEかどうか)値を変えてセットするやり方がメジャーなのかと思います。

 

また、調べて出てくるサイトの中では
Struts2でPoiを使ったダウンロード - QA@IT
が一番役立ちますが、これでは日本語ファイル名ダメでした。

ファイル名をISO-8859-1でエンコード

一律
this.filename = new String(filename.getBytes("MS932"), "ISO-8859-1");
で解消しました。
ExcelはISO-8859-1じゃないとダメらしいです。

IE分岐については自分らの環境では大丈夫でしたが一応テストした方が良さそうです。