特定のファイルをダウンロードさせる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(); ?>