一般的に、ドキュメント ルートの下にあるファイルへの URL を直接指定することで、ユーザーがファイルをダウンロードするように誘導できます。
ただし、これを行うと、統計や権限チェックなどを行うことができなくなります。そのため、多くの場合、PHP を使用して転送を行い、ユーザーにファイルのダウンロードを提供します。
$file = "/tmp/dummy.tar.gz";
header("Content-type: application/octet-stream");
header('Content-Disposition:attachment; filename="' .basename($file) .'"');
header("Content-Length: ".filesize($file));
readfile($file);
しかし、これには問題があります。ファイルの名前が中国語の場合、一部のユーザーはファイル名が文字化けしてダウンロードする可能性があります。
そこで、いくつかの変更を加えてみましょう (参考:
$file = "/tmp/中文名.tar.gz";
$filename = ベース名($file);
header("Content-type: application/octet-stream");
//中国語のファイル名を処理します
$ua = $_SERVER["HTTP_USER_AGENT"];
$encoded_filename = urlencode($filename);
$encoded_filename = str_replace("+", "%20", $encoded_filename);
if (preg_match("/MSIE/", $ua)) {
header('Content-Disposition:attachment; filename="' . $encoded_filename . '"');
} else if (preg_match("/Firefox/", $ua)) {
header("Content-Disposition:attachment; filename*="utf8''" . $filename . '"');
} else {
header('Content-Disposition:attachment; filename="' . $filename . '"');
}
header('Content-Disposition:attachment; filename="' . $filename . '"');
header("Content-Length: ".filesize($file));
readfile($file);
出力時に、Apache + PHP mod の場合は、Apache の出力バッファに送信する必要があります。Nginx + fpm の場合、それらが別々にデプロイされている場合、追加のネットワーク IO も発生します。 .
見た目はかなり良くなりましたが、まだ問題があり、それは PHP の readfile です。PHP の readfile は可能な限り効率的であり、PHP 自体のメモリを占有しないように努めていますが、実際には MMAP (サポートされている場合) を使用する必要があります。または、ファイルを周期的に読み取り、直接出力するために固定バッファーが使用されます。
では、Web サーバーは PHP 層を経由せずにユーザーに直接ファイルを送信できますか?
今日、興味深い記事を見つけました: How I PHP: X-SendFile.
Apache のモジュール mod_xsendfile を使用すると、Apache がこのファイルをユーザーに直接送信できるようになります:
$file = "/tmp/中文名.tar.gz";
$filename = ベース名($file);
header("Content-type: application/octet-stream");
//中国語のファイル名を処理します
$ua = $_SERVER["HTTP_USER_AGENT"];
$encoded_filename = urlencode($filename);
$encoded_filename = str_replace("+", "%20", $encoded_filename);
if (preg_match("/MSIE/", $ua)) {
header('Content-Disposition:attachment; filename="' . $encoded_filename . '"');
} else if (preg_match("/Firefox/", $ua)) {
header("Content-Disposition:attachment; filename*="utf8''" . $filename . '"');
} else {
header('Content-Disposition:attachment; filename="' . $filename . '"');
}
header('Content-Disposition:attachment; filename="' .basename($file) .'"');
//Xsendfile にファイルを送信させます
header("X-Sendfile: $file");
Lighttpd と Nginx にも同様のモジュールがありますので、興味があればチェックしてみてください
X-Sendfile ヘッダーは Apache によって処理され、応答ファイルはクライアントに直接送信されます。