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

Struts2で日本語ファイル名のファイルダウンロードがえっらいハマったのでメモです。

#streamのparamについて
単純にファイルをダウンロードするだけであれば、streamのパラメタを以下のようにすればダウンロードできます。

```java
@Result(name = "success", type = "stream",
 params = { "inputName", "inputStream",
  "contentType", "application/octet-stream; charset=UTF-8",
  "contentLength", "${ contentLength }",
  "contentDisposition", "attachment; filename = ${fileName}"
 }
)
```

`fileName`にファイル名を指定してやればいいのですが、2バイト文字が含まれるとブラウザ側で化けます。
これを回避するにはブラウザを判定して適切にファイル名をエンコードする必要がありますが、この`fileName`だけでは足りませんので、パラメタを以下のようにします。

```java
@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`に指定するファイル名をブラウザごとにエンコードしてやる必要があります。

#サンプル
以下、ファイルダウンロードアクションのサンプルです。


```java
@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
これ書くの忘れてた・・・

```xml
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>
```

を

```xml
<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の開発環境をそのまま使用しているとデフォルト値**なので、これ設定しないとだめだったわ。


以上!