ファイルを強制ダウンロードさせるヘッダについて調べた
一般的なブラウザは、拡張子が html だったり jpg だったり pdf だったりといった表示可能なファイルを受け取ると、
問題なければそのままタブ内に表示しようとします。これをなんとかやめさせたい。何卒名前をつけて保存してほしい。
というわけでファイルのダウンロードを強制するレスポンスヘッダについて調べたところ色んな書き方があったので整理してまとめておきます。
ダウンロード形式に関わるヘッダは、 Content-Type
Content-Disposition
の2つがあり、
調べていると以下の3つの指定がよく使われているのが見つかります。
- Content-Type: application/force-download
- Content-Type: application/octet-stream
- Content-Disposition: attachment
Content-Type: application/force-download
Content-Type の MIMEタイプに application/force-download
を指定すると、ファイル形式に関係なくダウンロードを開始させられます。
<?php header('Content-Type: application/force-download'); $file_path = './test.txt'; readfile($file_path);
一方で、force-download
というMIMEタイプは正式には存在せず、IANAのMIMEタイプ一覧にも載っていません。
https://www.iana.org/assignments/media-types/media-types.xhtml
force-download
は慣習的に使われているMIMEタイプであり、公式に定義されたものではありません。
なぜ強制ダウンロードされるかというと、Content-Type に未知のMIMEタイプを指定すると、当該ファイルを実行しようとせずダウンロードさせるというブラウザ側の仕様によります。
実際 force-download
を使わず、hoge
や foo
など適当な文字列を指定しても強制ダウンロードになります。
<?php // 適当なMIMEタイプを指定しても強制ダウンロードになる。 header('Content-Type: application/kinoko_vs_takenoko'); $file_path = './test.txt'; readfile($file_path);
当初の要件は満たせるものの、なんとなく気持ち悪さは残ります。
Content-Type: application/octet-stream
application/force-download
の代わりに application/octet-stream
を指定してもファイル形式に関係なくダウンロードを開始させられます。こちらは任意のバイナリデータを表す、公式なMIMEタイプです。
application/octet-stream
これは、バイナリファイルでは既定です。これは未知のバイナリ形式のファイルを表すものであり、ブラウザーはふつう実行したり、実行するべきか確認したりしません。https://developer.mozilla.org/ja/docs/Web/HTTP/Basics_of_HTTP/MIME_types
<?php header('Content-Type: application/octet-stream'); $file_path = './test.txt'; readfile($file_path);
application/force-download
よりはまだしもですが、本来通知すべきMIMEタイプを指定していないという意味ではハックな感じがして、
これまたなんとなく気持ち悪さは残ります。
Content-Disposition: attachment
Content-Disposition
は、コンテンツをwebページの一部として表示するかダウンロードするかを示します。inline
を指定すればwebページとして、attachment
を指定すればダウンロードファイルとして、を表します。また、filenameパラメータで、ファイルの初期名を指定できます。
<?php header('Content-Disposition: attachment; filename="hoge.txt"'); $file_path = './test.txt'; readfile($file_path); // ファイル名 hoge.txt としてダウンロードされる。
なんとなくよさげな感じがしますね。しかしContent-Disposition
はHTTP1.1標準のヘッダではありません。
HTTP/1.1: Appendices 19.5
現代のブラウザのほとんどは対応していますが、古いIEだとこのヘッダ自体を無視してしまいます。humm..
まとめ
どの方法もつつけばモヤモヤする部分はありますが、まとめると
- 基本は、
Content-Disposition: attachment
を指定する。 - 古いブラウザに対応しなければいけないときは、
Content-Type: application/octet-stream
を合わせて指定する。
としておくのが良いかと思います。