特定のファイルをダウンロードさせるPHPスクリプト
特定のディレクトリ以下の有るファイルをブラウザからダウンロードさせるPHPスクリプト。
たまに、PHP-users ML とか出てくる FAQ 的な話なんだけど、
- Content-Disposition ヘッダの書式
- output buffering のクリア
- でかいファイルのときに readfile 使うとメモリ制限にひっかかるために chunk に分けて出力する
とか、細かいことがあるのでメモ。
<?php
define('FILE_DIR', 'files');
function quote_string($str)
{
$search = array('\\', '\"');
$replace = array('\\\\', '\\\"');
return str_replace($search, $replace, $str);
}
function path_join()
{
$args = func_get_args();
return implode(DIRECTORY_SEPARATOR, $args);
}
function readfile_chunked($filename, $chunksize=8192)
{
$buffer = '';
$cnt = 0;
$fp = fopen($filename, 'rb');
if ($fp == false) {
return false;
}
while (!feof($fp)) {
$buffer = fread($fp, $chunksize);
echo $buffer;
flush();
$cnt += strlen($buffer);
}
fclose($fp);
return $cnt;
}
function main()
{
$file = $_GET['file'];
if (strstr('/', $file) !== false || $file[0] == '.') {
header('HTTP/1.0 403 Forbidden');
exit();
}
$filename = path_join(FILE_DIR, $file);
if (!file_exists($filename)) {
header('HTTP/1.0 404 Not Found');
exit();
} elseif (!is_readable($filename)) {
header('HTTP/1.0 403 Forbidden');
exit();
}
$content_disposition
= sprintf('attachment; filename="%s"', quote_string($file));
header('Content-type: application/download');
header('Content-Disposition: ' . $content_disposition);
header('Content-Length: ' . filesize($filename));
if (in_array($_SERVER['REQUEST_METHOD'], array('GET', 'POST'))) {
flush();
while (ob_get_level()) {
ob_end_flush();
}
readfile_chunked($filename);
}
}
main();
?>