php 7.4 文件下载需正确设置响应头、保障路径安全并避免缓冲干扰:用 content-disposition: attachment 触发下载,中文文件名需 rawurlencode 双重兼容;禁止直接使用用户输入的文件名,须白名单校验+realpath 路径约束;输出前调用 ob_end_clean() 清空缓冲,用 readfile() 或 fpassthru() 输出文件,最后 exit 终止脚本。

PHP 7.4 实现文件下载到本地,核心是正确设置 HTTP 响应头 + 输出文件内容,关键在避免缓冲干扰、路径安全和中文名乱码。下面直接说重点。
设置正确的响应头,触发浏览器下载
必须用 Content-Disposition: attachment 告诉浏览器“这不是展示,是下载”。同时指定文件名(尤其注意中文名要 URL 编码),并禁用缓存:
- header('Content-Type: application/octet-stream'); —— 通用二进制类型,兼容所有文件
-
header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"; filename*=UTF-8\'\'' . rawurlencode($filename)); —— 双重兼容:旧浏览器读
filename,新浏览器(Chrome/Firefox)优先读filename*,解决中文名乱码 - header('Content-Transfer-Encoding: binary'); —— 明确传输编码
-
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public'); —— 强制不缓存,防止下载旧文件
安全读取文件,杜绝路径遍历漏洞
用户传来的文件名不能直接拼接路径!否则可能通过 ../../etc/passwd 下载系统文件:
- 禁止接收原始文件名参数(如
$_GET['file'])直接使用 - 用白名单或映射方式限定可下载文件:
$allowed = ['report.pdf', 'data.xlsx'];
if (!in_array($_GET['id'], $allowed)) { die('Invalid file'); }
$filepath = '/var/www/files/' . $_GET['id']; - 务必用 realpath() 检查路径是否仍在预期目录内:
$realpath = realpath($filepath);
$base_dir = '/var/www/files/';
if ($realpath === false || strpos($realpath, $base_dir) !== 0) { die('Access denied'); }
输出文件内容,关闭缓冲干扰
PHP 输出缓冲(output buffering)会导致下载失败或文件损坏,必须清理并关闭:
- 调用 ob_end_clean(); 清空已有缓冲(放在 header() 之前)
- 确保没有 echo/print 在 header() 前输出(包括 BOM、空格、换行)
- 用 readfile($filepath) 直接输出(适合中小文件);大文件建议用 fopen + fpassthru 流式读取,避免内存溢出
- 最后加 exit; 阻止后续代码执行
完整示例(带错误处理)
php
// 示例:下载 /files/report_2024.pdf
$id = $_GET['id'] ?? '';
$allowed = ['report_2024.pdf', 'summary.xlsx'];
if (!in_array($id, $allowed)) {
http_response_code(404);
die('File not found');
}
$base_dir = __DIR__ . '/files/';
$filepath = $base_dir . $id;
$realpath = realpath($filepath);
if ($realpath === false || strpos($realpath, $base_dir) !== 0) {
http_response_code(403);
die('Access denied');
}
$filename = basename($id);
ob_end_clean();
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"; filename*=UTF-8\'\'' . rawurlencode($filename));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($filepath));
readfile($filepath);
exit;
?>
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











