php 8.0 实现文件下载需设置 content-type、content-disposition 和 content-length 响应头,用 readfile() 安全输出文件流,并防范路径遍历与输出干扰;推荐生产环境使用 x-sendfile 交由 web 服务器处理。

PHP 8.0 本身不直接“下载文件到本地”,而是通过 HTTP 响应头 + 文件内容输出,让浏览器触发下载行为。关键在于正确设置响应头、读取服务器上的文件,并输出二进制流(不能有额外空格或错误输出)。
设置正确的响应头
这是下载功能的核心。必须设置以下三个关键 Header:
- Content-Type: application/octet-stream(通用二进制类型,避免浏览器尝试解析)
- Content-Disposition: attachment; filename="xxx.ext"(强制下载并指定保存名)
- Content-Length: 文件字节数(提升体验,支持断点续传和进度显示)
安全读取并输出文件内容
推荐使用 readfile()(内存友好)或 fopen()+fpassthru()(适合大文件)。务必检查文件是否存在、是否可读,且路径不能被用户控制(防目录遍历攻击)。
示例代码(安全、简洁、兼容 PHP 8.0):
<?php // 示例:下载服务器上 /var/www/files/report.pdf
$filePath = '/var/www/files/report.pdf';
// 1. 安全校验(路径固定或白名单验证,禁止用户输入拼接)
if (!is_file($filePath) || !is_readable($filePath)) {
http_response_code(404);
die('File not found or inaccessible.');
}
// 2. 获取文件信息
$fileName = basename($filePath);
$fileSize = filesize($filePath);
// 3. 设置响应头
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Content-Length: ' . $fileSize);
header('Cache-Control: private'); // 防止代理缓存敏感文件
header('Pragma: private');
// 4. 清空输出缓冲区(防止之前 echo 或 warning 干扰)
if (ob_get_level()) {
ob_end_clean();
}
// 5. 输出文件内容(不加载进内存,适合大文件)
readfile($filePath);
exit;
?>
常见问题与注意事项
-
空白字符/Warning 导致下载失败:确保 PHP 文件无 BOM、前后无空行;开启
display_errors=Off或用@抑制非致命警告(不推荐) -
中文文件名乱码:需对 filename 进行编码,如:
header('Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode($fileName)); -
大文件超时/内存溢出:避免
file_get_contents();用readfile()或分块读取(fopen+while+fread+echo) -
下载后页面空白是正常现象:因为脚本已输出文件流并
exit,不再渲染 HTML
替代方案:用 X-Sendfile(Nginx/Apache)
生产环境更推荐由 Web 服务器处理文件传输(节省 PHP 资源)。例如 Nginx 配置启用 X-Accel-Redirect 后,PHP 只需:
header('X-Accel-Redirect: /internal/files/report.pdf'); // 内部别名路径
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="report.pdf"');
exit;
此时 PHP 不读文件,仅发指令,性能更高、更安全。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











