PHPからのダウンロード処理

テーマ:

特定の人にだけファイルをダウンロードさせたいというときにはPHPスクリプトをダウンロード対象指定で呼び出して、そこでアクセス権をチェックできたらダウンロード対象のファイルデータを返すという風にする必要がある。



例えば、GET送信で
<a href="download.php?file=target.zip">ダウンロード</a>
と書いたり、拡張パスで指定して
<a href="download.php/target.zip">ダウンロード</a>
のようなイメージになる。



取得したダウンロード対象ファイルパスは必ずチェックしないといけない。
$_GET['file']がtarget.zipやimg/01.jpgなどなら想定内だが、もし
.htpasswdやら/etc/passwdやら/var/mail/userなどダウンロードされたくないファイルが指定されて呼び出されてしまったら困るのでこの部分のチェックはその環境に応じてしっかりやる必要がある。



PHPでダウンロード処理を行うにはまず適切なヘッダを返さないとダウンロードが始まらない。
ところがこの「適切なヘッダ」というものがはっきりしなかったりする。
理由はユーザの使うブラウザの挙動がメーカーや動作環境やバージョンによりまちまちだからである。
また、ダウンロード開始時に出てきた保存用ダイアログに、意図するファイル名ではなくdownload.phpなどのファイル名でセットされていたりする場合はヘッダでこのときのファイル名を指定できる場合があるが、一切ヘッダではコントロールできないブラウザ環境も存在する。その場合は拡張パスなどでブラウザをダマす回避策をとるしかない恐れがある。




とりあえず無理を承知でダウンロード部分のコーディングをしてみる。
<?php

$realfilepath= "download.zip"; // $_GET['file']や$_SERVER['PATH_INFO']から取得した値を想定
if (!file_exists($realfilepath)) die("処理に失敗しました。");
$encfile= basename($realfilepath);
$ua= $_SERVER['HTTP_USER_AGENT'];


if (strpos($ua, "MSIE") >=0 && strpos($ua, "Win") >=0 && !(strpos($ua, "Opera") >= 0)){
    header('Cache-Control: public');
    header('Pragma: public');
    $encfile= urlencode(mb_convert_encoding($encfile, "SJIS", "SJIS,UTF-8,EUC-JP,JIS"));
} else {
    header("Cache-Control: no-cache, must-revalidate");
    header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
    if (!(strpos($ua, "Opera") >=0)) header("Content-Type: application/force-download");
    if (!(strpos($ua, "Win") >=0) && preg_match("/MSIE|Safari|Konqueror/", $ua))
        $encfile= '';
    else if (preg_match('/[\x1b\x80-\xff]/', $encfile))
        $encfile= mb_encode_mimeheader(mb_convert_encoding($encfile, "UTF-8", "SJIS,UTF-8,EUC-JP,JIS"), "UTF-8", "B");
}


header("Content-Disposition: attachment; filename=\"{$encfile}\"");
header("Content-Type: application/octet-stream");
header("Content-Length: " . filesize($realfilepath));


readfile($realfilepath);
exit;


?>


充分なテスト環境が揃えられないので動作チェックができていない。まともに動かないだろう。。
OparaにはユーザエイジェントをIE偽装をする設定があるらしい。
WindowsではシフトJISのファイル名じゃないと化けるらしい。


[参考サイト]
http://www.geocities.jp/ht_deko/ft0608.html
http://oku.edu.mie-u.ac.jp/~okumura/php/filename.php



【結論】
ダウンロードさせるファイルに日本語を使っても様々なダウンロード環境があるので文字化けになる確率が非常に高い。
PHPマニュアルに載っているダウンロードのコードを見ても、欧米用のアスキー文字を想定してしか書いてないようである。
ダウンロード以外でも扱うファイル名には日本語文字は避けて、アスキー文字を使うようにしましょ