Tomcat, Apacheを連携してWebシステムで、ファイルをダウンロードさせるときの設定をまとめました。
【確認した環境】
確認したときの環境
- Firefox50
- Windows7
- Java8
- Servlet v3
- Apache 2.2
- Tomcat7, Tomcat8
Servletでダウンロード
コンテンツタイプをapplication/octet-stream
にする
response.setContentType("application/octet-stream");
response.getWriter().write("Hello World");
これは、バイナリー形式ファイル用の既定の値です。実際は未知のバイナリー形式ファイルを表しており、通常ブラウザーは自動的に実行したり、実行すべきであるかを確認したりしません。これらは Content-Disposition ヘッダーの値が attachment であるかのように扱い、ファイルを '名前を付けて保存' することを提案します。
Content-Dispositionヘッダーの値をattachment
にする
前述の通り、コンテンツタイプにapplication/octet-stream
を指定すればダウンロードできます。
しかし、ダウンロード対象のファイルの種類が分かっている場合、application/octet-stream
を指定するのは、何だか違和感を感じます。
下記の通り、Content-Dispositionヘッダーにattachment
を指定すれば、コンテンツタイプが"application/octet-stream"でなくても、ダウンロードできます。
response.setContentType("text/html");
response.setHeader("Content-Disposition","attachment");
response.getWriter().write("Hello World");
ダウンロード時のデフォルトのファイル名
filename
に指定した値が、ダウンロード時のデフォルトのファイル名になります。
response.setHeader("Content-Disposition","attachment;filename=\"sample.html\"");
// attachment;filename="sample.html"
When used in combination with Content-Disposition: attachment, it is used as the default filename for an eventual 'Save As" dialog presented to the user.
ファイル名に全角文字が含まれているときは、filename*
を使います。
String encodedFilename = URLEncoder.encode("サンプル.html", "UTF-8");
response.setHeader("Content-Disposition","attachment;filename*=\"UTF-8''" + encodedFilename + "\"");
The parameters "filename" and "filename*" differ only in that "filename*" uses the encoding defined in RFC 5987. When both "filename" and "filename*" are present in a single header field value, "filename*" is preferred over "filename" when both are present and understood.
半角空白文字はプラス記号「+」に変換されることに、注意してください。
以下のコードは、ファイル名がa+b.html
になります。
String encodedFilename = URLEncoder.encode("a b.html", "UTF-8");
response.setHeader("Content-Disposition","attachment;filename*=\"UTF-8''" + encodedFilename + "\"");
これは、JavaのURLEncoderの仕様です。
https://docs.oracle.com/javase/jp/8/docs/api/java/net/URLEncoder.html
空白文字「 」はプラス記号「+」に変換されます。
「+」をさらに%20
で置換すれば、ファイル名に空白文字が表示されます。
ちなみにURLEncodeは言語によって方言があるので、深入りしないことにします。。。
https://www.glamenv-septzen.net/view/1170
ファイル名に使えない文字はアンダースコアに変換されます。
以下のコードは、ファイル名が_________.html
になります。
// ¥ / : * ? " < > | を含むファイル名
String encodedFilename = URLEncoder.encode("\\/:*\"<>|.html", "UTF-8");
response.setHeader("Content-Disposition","attachment;filename*=\"UTF-8''" + encodedFilename + "\"");
filename
とfilename*
の併記
以下のように filename
とfilename*
を併記すると、filename*
が有効な場合はfilename
が無視されます。
filename*
に対応していないブラウザを考慮するならば(2017年にそんなブラウザはある?)、併記するのがよさそうです。
String encodedFilename = URLEncoder.encode("サンプル.html", "UTF-8");
response.setHeader("Content-Disposition","attachment;" +
"filename=\"sample.html\"" +
"filename*=\"UTF-8''" + encodedFilename + "\"");
Therefore, when both "filename" and "filename*" are present in a single header field
value, recipients SHOULD pick "filename*" and ignore "filename".
※ https://tools.ietf.org/html/rfc6266#section-4.5 引用
※ ダウンロードファイル名、文字化けとの格闘 参考
filaname*
の二重引用符
RFC6266の例では、filename
には二重引用符が付いていますが、filename*
には付いていません。
Content-Disposition: attachment;
filename="EURO rates";
filename*=utf-8''%e2%82%ac%20rates
※ https://tools.ietf.org/html/rfc6266#section-5 引用
filename*
に二重引用符を付けた場合(filename*="utf-8''サンプル.html"
)、各ブラウザのダウンロード結果は以下の通りになりました。
- Firefox57: OK
- Chrome 63:NG
- Microsoft Edge 41.16299.15.0:NG
- Internet Explorer 11.125.16299.0:NG
Firefox以外では、filename*
を無視され、ファイル名が"download"でした。
※ https://qiita.com/khsk/items/d541b8dc40bd2c6128d2 参考
Apache配下のファイルをダウンロード
mod_headersを有効にして、Content-dispositionヘッダーを指定します。
以下のコードは、Apache配下の全てのxmlファイルがダウンロード対象になります。
LoadModule headers_module modules/mod_headers.so
<FilesMatch "\.(xml)$">
Header set Content-Disposition attachment
</FilesMatch>
【参考サイト】
http://qiita.com/kompiro/items/ac60721bc43625a057dc
http://sample.co.jp/sample.xml
にアクセスすれば、"sample.xml"というファイル名でダウンロードされます。
ファイル名がURLに含まれていることに注意してください。ファイル名にURLとして特別な意味を持つ文字が含まれていると、正しくダウンロードされません。以下の文字を含むファイル名は、正しくダウンロードされませんでした。(Firefoxで確認。ファイル名に使えない文字が、これで全てかどうかは不明)
# %
HTMLのa要素でダウンロード
a要素にdownload属性を指定すると、download属性に指定した値がファイル名となってダウンロードされます。全角文字や半角空白、URLとして特別な意味を持つ#
なども使えます。
ただしIEは未対応です…
<a href="sample.png" download="テストa b#%.png">Download</a>
この属性は、URL に移動するのではなくダウンロードするようブラウザーへ示しますので、ユーザーはローカルファイルとして保存することを促されます。属性に値を指定した場合、保存プロンプトのデフォルトのファイル名として解釈します (ユーザーは必要に応じてファイル名を変更できます)。使用可能な値に制限はありませんが、/ および \ はアンダースコアに変換します。多くのファイルシステムにはファイル名に使用できる文字の制限があり、ブラウザーがファイル名を調整するかもしれません。
<a href="sample.png" download="テストa b#%.png">Download</a>
download属性に ¥ / : * ? " < > |
を指定すると、全てアンダースコアに変換されました(Firefoxで確認)。
以下のコードはファイル名が_________.html
になります。
<a href="sample.png" download="\/:*?"<>|.png">Download</a>
Tomcat配下のファイルをダウンロードする際の注意
Tomcat7以前(?)は、URIEncoding
のデフォルトはISO-8859-1
です。この文字コードはラテンアルファベットのキャラクタセットで、日本語を表現できません。
This specifies the character encoding used to decode the URI bytes, after %xx decoding the URL. If not specified, ISO-8859-1 will be used.
そのため、日本語を含むファイルをダウンロードしたいときは、文字コードをUTF-8に設定する必要があります。
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8" /><!-- ← 追記 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"
URIEncoding="UTF-8" /><!-- ← 追記 -->
Tomcat8では、デフォルトがUTF-8なので特に指定は必要ありません。
This specifies the character encoding used to decode the URI bytes, after %xx decoding the URL. If not specified, UTF-8 will be used unless the org.apache.catalina.STRICT_SERVLET_COMPLIANCE system property is set to true in which case ISO-8859-1 will be used.
【参考サイト】
http://qiita.com/kazuki43zoo/items/a365d194f5c4df28912f