サーバ側で対応する (基本)
例えば PHP で以下のようなプログラムを用意します。
仮にこれを download.php とします。
header('Content-Type: application/pdf');
header('Content-Length: '.filesize('sample.pdf'));
header('Content-Disposition: attachment; filename="sample.pdf"');
echo file_get_contents('sample.pdf');
exit;
HTML には以下のように記述します。
<a href="download.php"a>sample.pdf</a>
サーバには sample.pdf をアップロードしておきます。
こうすることで、クリック時に PDF、画像、テキストなどを
ブラウザ内で開くのではなくダウンロードできるようになります。
サーバ側で対応する (実践)
ダウンロード対象のファイルが複数種類、複数個ある場合
を考えますと、先のような作りだとちょっと煩雑かもしれません。
download.php がファイル数分増えると面倒なので、
例えば、以下のようにクエリ (?key=myKey1) で
ダウンロード対象を指定できるように考えてみます。
<a href="download.php?key=myKey1"a>sample.pdf</a>
例えば以下のように 1 なら sample.pdf、
2 なら sample.png といった感じで
リストを作って置けば簡単でしょうか。
$key_2_content = array(
'1' => array(
'path' => './sample.pdf'
),
'2' => array(
'path' => './sample.png'
),
'3' => array(
'path' => './sample.txt'
)
);
1 とか 2 だとちょっと手が滑るなどの軽微な打ち間違いで
間違ったファイルがダウンロードされてしまう可能性がでてきます。
そこで、key には ibrbLaBSPAWiSmis のような
混同の恐れの低いものを割り当てることにして
トラブルになり難いように保険をかけておきます。
$key_2_content = array(
'ibrbLaBSPAWiSmis' => array(
'path' => './sample.pdf'
),
'YPMPeAN2i6ymVxwp' => array(
'path' => './sample.png'
),
'J6XNegQDhdBhTibr' => array(
'path' => './sample.txt'
)
);
サーバ内でのファイル名を知られたくないという話が
後から出てくるとめんどうなので、保険ついでに
ダウンロード時のファイル名と
サーバ内でのファイル名 (=pathの一部) を
独立無関係なものにしておきます。
$key_2_content = array(
'ibrbLaBSPAWiSmis' => array(
'path' => './XYQRuXuVwDdnhbKT',
'name' => 'sample',
'ext' => 'pdf'
),
'YPMPeAN2i6ymVxwp' => array(
'path' => './MT4yK7h7Q2sgPdZX',
'name' => 'sample',
'ext' => 'png'
),
'J6XNegQDhdBhTibr' => array(
'path' => './DAJGVRH5YDM2jWgz',
'name' => 'sample',
'ext' => 'txt'
)
);
以下をクリックすると ./XYQRuXuVwDdnhbKT が
sample.pdf という名前でダウンロードされるという考えです。
<a href="download.php?key=ibrbLaBSPAWiSmis"a>sample.pdf</a>
拡張子から MIMEタイプをさっと得られるように
よく使いそうなものを軽く列挙しておきます。
$ext_2_mime = array(
'txt' => 'text/plain',
'gif' => 'image/gif',
'jpg' => 'image/jpeg',
'png' => 'image/png',
'pdf' => 'application/pdf',
'zip' => 'application/zip'
);
クエリ (key) が規定したものである場合のみ
ファイルがダウンロードされるように簡単にチェックしておきます。
規定外の場合は何もダウンロードされません。
$key = $_REQUEST['key'];
if (!array_key_exists($key, $key_2_content)) { exit; }
パス、ファイル名、MIMEタイプなど、
クエリ (key) に対応するデータを特定します。
$content = $key_2_content[$key];
$path = $content['path'];
$name = $content['name'];
$ext = $content['ext'];
$filename = $name . '.' . $ext;
$mime = $ext_2_mime[$ext];
ダウンロード対象のファイルを返します。
header('Content-Type: ' . $mime);
header('Content-Length: '.filesize($path));
header('Content-Disposition: attachment; filename="'.$filename.'"');
readfile($path); // echo file_get_contents($path); だとメモリを喰います。
exit;
サーバには sample.pdf としてダウンロードさせたいものを
XYQRuXuVwDdnhbKT という名前でアップロードしておきます。
アップロードの際、FTPの転送モードに気をつけて下さい。
ファイルの拡張子から転送モードが自動判定される場合、
Binary ではなく ASCII モードでアップロードされて
問題を生じることがあるかもしれません。
以上、すべてをつなげると以下のようなコードになります。
$key_2_content = array(
'ibrbLaBSPAWiSmis' => array(
'path' => './XYQRuXuVwDdnhbKT',
'name' => 'sample',
'ext' => 'pdf'
),
'YPMPeAN2i6ymVxwp' => array(
'path' => './MT4yK7h7Q2sgPdZX',
'name' => 'sample',
'ext' => 'png'
),
'J6XNegQDhdBhTibr' => array(
'path' => './DAJGVRH5YDM2jWgz',
'name' => 'sample',
'ext' => 'txt'
)
);
$ext_2_mime = array(
'txt' => 'text/plain',
'gif' => 'image/gif',
'jpg' => 'image/jpeg',
'png' => 'image/png',
'pdf' => 'application/pdf',
'zip' => 'application/zip'
);
$key = $_REQUEST['key'];
if (!array_key_exists($key, $key_2_content)) { exit; }
$content = $key_2_content[$key];
$path = $content['path'];
$name = $content['name'];
$ext = $content['ext'];
$filename = $name . '.' . $ext;
$mime = $ext_2_mime[$ext];
header('Content-Type: ' . $mime);
header('Content-Length: '.filesize($path));
header('Content-Disposition: attachment; filename="'.$filename.'"');
readfile($path);
exit;
サンプルを設置してみたので、以下をクリックすると
それぞれサンプルファイルがダウンロードできると思います。
sample.pdf
sample.png
sample.txt
大きなファイルを扱う場合で
out of memoryエラーが出るなら
以下のように出力バッファを無効化したり
while (ob_get_level()) {
ob_end_clean();
}
処理時間が足りなければ
以下のように実行時間の制限を緩和します。
set_time_limit(300); // ゼロに設定すると時間制限がなくなります。
クライアント側で対応する (基本)
以下のように a要素に download属性を付与することで
ファイルをダウンロードするようになります。
ファイルサイズとバッファやプログラムの実行時間など、
プログラム的な考慮事項が少なくてすみます。
<a href="http://www.webmagazine.kakisiti.co.jp/wak-sample/p690/sample.pdf" download="sample.pdf">sample.pdf</a>
ただし、Safari や IE は download属性に対応していません。
以下は download属性によるサンプルです。
sample.pdf
sample.png
sample.txt
クライアント側で対応する (実践)
download属性がサポートされていないブラウザでは
クリックしても基本的にダウンロードされません。
以下のように download属性のサポートをチェックして、
サポートされていない場合には a要素の class属性値に
no-download-attr-support などと付けます。
var hasDownloadAttrSupport = ('download' in document.createElement('a'));
if (!hasDownloadAttrSupport) {
$.each($('a[download]'),function(){
$(this).addClass('no-download-attr-support');
});
}
以下のような CSS を用意することで
注意書きを付置することができます。
a.no-download-attr-support:after { content: '右クリックやロングタップしてダウンロードしてください'; }
より具体的に、多少の位置調整などもすると以下のような感じで書けます。
a { position: relative }
a.no-download-attr-support:after { content: '右クリックやロングタップしてダウンロードしてください'; display: block; position: absolute; left: 0; bottom: -20px; color: gray; font-size: 10px; line-height: 15px; width: 280px; }
すると、以下のように注記を付けることができます。
最後に
iOS の Safari など、そもそも設計上、
ファイルをダウンロードして任意のアプリで開く
といった使い方がなじまないものもあります。
モバイルまで含めて、使い勝手と
表現の一貫性を追求するのであれば
クリックでダウンロードという表現自体を
控えた方が良いと言えるかもしれません。
お問い合わせについて
業務として技術コンサルティングやシステム設計・開発を行っております。
気になることがありましたらご相談下さい。
ご相談のみで完結する場合、コンサルティング費用の目安は
内容によりますが1時間で5千円〜1万円ていどです。
コンサルティングや開発を検討されるその前に、
まずはお気軽にコメントやメールでご連絡下さい。
※ご契約前のコメントやメールでのやりとりは無料です。