LoginSignup
95

More than 3 years have passed since last update.

ファイルをダウンロードさせる方法(Servlet, HTML, Apache, Tomcat)

Last updated at Posted at 2017-01-29

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 + "\"");

filenamefilename*の併記

以下のように filenamefilename*を併記すると、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="&#x5c;/:*?&quot;&lt;&gt;|.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に設定する必要があります。

server.xml
<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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
95