php怎么实现下载
实现文件下载的基本方法
在PHP中实现文件下载通常通过设置HTTP头信息强制浏览器触发下载行为。核心代码如下:
$file_path = '/path/to/file.pdf';
$file_name = 'document.pdf';
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($file_name).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file_path));
readfile($file_path);
exit;
处理大文件下载
对于大文件下载需要优化内存使用,避免直接读取整个文件:

$chunk_size = 1024 * 1024; // 1MB chunks
$handle = fopen($file_path, 'rb');
while (!feof($handle)) {
echo fread($handle, $chunk_size);
ob_flush();
flush();
}
fclose($handle);
安全下载控制
为防止目录穿越攻击,应对文件路径进行验证:
$base_dir = '/safe/download/directory/';
$requested_file = $_GET['file'];
$real_path = realpath($base_dir . $requested_file);
if ($real_path === false || strpos($real_path, $base_dir) !== 0) {
die('Invalid file path');
}
断点续传支持
实现HTTP Range请求支持断点续传:

$file_size = filesize($file_path);
$start = 0;
$end = $file_size - 1;
if (isset($_SERVER['HTTP_RANGE'])) {
preg_match('/bytes=(\d+)-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches);
$start = (int)$matches[1];
$end = isset($matches[2]) ? (int)$matches[2] : $file_size - 1;
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes '.$start.'-'.$end.'/'.$file_size);
}
header('Accept-Ranges: bytes');
header('Content-Length: '.($end - $start + 1));
$handle = fopen($file_path, 'rb');
fseek($handle, $start);
下载统计与限速
添加下载统计和速度限制功能:
$speed_limit = 1024 * 50; // 50KB/s
$start_time = microtime(true);
$bytes_sent = 0;
while (!feof($handle) && connection_status() == 0) {
$buffer = fread($handle, $speed_limit);
echo $buffer;
$bytes_sent += strlen($buffer);
$elapsed = microtime(true) - $start_time;
$should_take = $bytes_sent / $speed_limit;
if ($elapsed < $should_take) {
usleep(($should_take - $elapsed) * 1000000);
}
ob_flush();
flush();
}
常见问题处理
处理文件名编码问题:
$user_agent = $_SERVER['HTTP_USER_AGENT'];
$encoded_filename = rawurlencode($file_name);
if (preg_match('/MSIE|Trident/i', $user_agent)) {
header('Content-Disposition: attachment; filename="'.iconv('UTF-8', 'CP932//IGNORE', $file_name).'"');
} elseif (preg_match('/Firefox/i', $user_agent)) {
header('Content-Disposition: attachment; filename*=UTF-8\'\''.$encoded_filename);
} else {
header('Content-Disposition: attachment; filename="'.$file_name.'"');
}






