php 8.1 文件下载需设置正确响应头并用readfile()输出,关键在于content-type、content-disposition、content-length等头信息,配合路径校验、mime推断和中文名编码处理。

PHP 8.1 中实现文件下载到本地,核心是设置正确的 HTTP 响应头,并用 readfile() 或 fpassthru() 输出文件内容。关键不是 PHP 版本差异(8.1 和之前版本操作一致),而是确保响应头不被缓冲、路径安全、文件存在且可读。
设置响应头强制浏览器下载
必须发送以下关键响应头,否则浏览器可能直接在页面显示文本/图片,而不是弹出保存对话框:
- Content-Type: application/octet-stream —— 通用二进制流类型,避免 MIME 类型触发内嵌显示
-
Content-Disposition: attachment; filename="xxx.ext" ——
attachment强制下载;filename建议使用 ASCII 字符,中文名需 UTF-8 编码并加filename*=UTF-8''... - Content-Length: 文件字节数 —— 提升体验,支持断点续传和进度显示
- Cache-Control: no-cache, must-revalidate, max-age=0 —— 防止代理或浏览器缓存旧文件
基础安全下载示例(推荐)
以下代码适用于 PHP 8.1,已包含路径校验、MIME 推断、中文文件名兼容处理:
<?php $filePath = '/var/www/files/report.pdf'; // 实际服务器绝对路径
$fileName = '2024年度报告.pdf'; // 用户看到的下载文件名(可含中文)
// 1. 安全校验:确保文件存在、可读、且不在敏感目录(如防止 ../../etc/passwd)
if (!is_file($filePath) || !is_readable($filePath)) {
http_response_code(404);
die('文件不存在或不可读');
}
// 2. 获取真实文件大小和 MIME 类型(更准确 than extension-based)
$fileSize = filesize($filePath);
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $filePath) ?: 'application/octet-stream';
finfo_close($finfo);
// 3. 清空输出缓冲,防止 header 已发送错误
if (headers_sent($file, $line)) {
die("Headers already sent in $file on line $line");
}
ob_end_clean();
// 4. 发送响应头
header('HTTP/1.1 200 OK');
header('Content-Type: ' . $mimeType);
header('Content-Length: ' . $fileSize);
header('Content-Disposition: attachment; filename="' . rawurlencode($fileName) . '"');
header('Content-Transfer-Encoding: binary');
header('Cache-Control: no-cache, must-revalidate, max-age=0');
header('Expires: 0');
header('Pragma: no-cache');
// 5. 输出文件(不加载内存,适合大文件)
readfile($filePath);
exit;
?>
注意事项与常见问题
-
路径必须是服务器本地绝对路径,不能是 URL(如
https://example.com/file.pdf);若需远程文件,请先用file_get_contents()或cURL下载到临时路径再提供 -
中文文件名在 Chrome/Firefox 支持
filename*=UTF-8''...格式,如:header('Content-Disposition: attachment; filename="' . rawurlencode($fileName) . '"; filename*=UTF-8\'\'' . rawurlencode($fileName)); -
大文件建议用
readfile()(内存友好),避免file_get_contents()导致 OOM;超大文件可配合set_time_limit(0)和分块读取(fopen+fread+flush) -
禁止用户直接传入文件路径参数(如
?file=../../etc/passwd),务必白名单校验或映射 ID → 路径,杜绝路径遍历漏洞
简单测试方式
将上述代码保存为 download.php,放在 Web 可访问目录,直接浏览器访问该 URL 即可触发下载。确保 $filePath 指向一个真实存在的、有读权限的文件。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











