php实现文件下载需设置content-type、content-disposition(含rawurlencode处理中文名)、content-length等响应头,校验文件存在性与可读性,用readfile()输出并清除缓冲;中文名须用filename*=utf-8''+rawurlencode()编码。

PHP 8.0 本身不直接“下载文件到本地”,而是通过 HTTP 响应头 + 文件内容输出,**让浏览器触发下载行为**。关键在于正确设置响应头(尤其是 Content-Disposition),并确保文件内容被安全、完整地输出。
设置强制下载响应头
这是核心步骤。需在输出文件内容前,用 header() 发送正确的 HTTP 头,告诉浏览器“这不是要显示的网页,而是要保存的文件”:
-
Content-Type: application/octet-stream:通用二进制流类型,适用于未知或任意文件 -
Content-Disposition: attachment; filename="xxx.ext":attachment触发下载;filename指定默认保存名(注意:中文名需 UTF-8 编码并加filename*=UTF-8''...) -
Content-Length: xxx:可选但推荐,提前告知文件大小,提升体验 -
Cache-Control: no-cache和Pragma: no-cache:防止浏览器缓存旧版本
安全读取并输出文件内容
避免直接暴露服务器路径或执行任意文件读取。推荐做法:
- 将待下载文件放在 Web 根目录外(如
/var/www/files/),或严格限制可访问路径 - 用
basename()过滤用户传入的文件名,防止路径遍历(如../../etc/passwd) - 使用
readfile()(内存友好)或fopen()+fpassthru()(适合大文件)输出内容 - 输出后调用
exit;或die();,防止后续代码干扰响应
完整可运行示例(含中文文件名支持)
假设你要提供 /var/www/uploads/report.pdf 下载,希望保存为“月度报告.pdf”:
<?php // 1. 设置文件路径(务必校验!)
$filePath = '/var/www/uploads/report.pdf';
$fileName = '月度报告.pdf'; // 用户看到的文件名
// 安全校验:检查文件是否存在、是否为合法路径
if (!is_file($filePath) || !is_readable($filePath)) {
http_response_code(404);
die('文件不存在或不可读');
}
// 2. 清除可能存在的输出缓冲
if (ob_get_level()) {
ob_end_clean();
}
// 3. 设置响应头
header('Content-Type: application/pdf'); // 更精确的 MIME 类型(PDF)
header('Content-Disposition: attachment; filename="' . rawurlencode($fileName) . '"; filename*=UTF-8\'\'' . rawurlencode($fileName));
header('Content-Length: ' . filesize($filePath));
header('Cache-Control: no-cache');
header('Pragma: no-cache');
// 4. 输出文件内容
readfile($filePath);
exit;
?>
说明:PHP 8.0 支持 rawurlencode() 直接处理 UTF-8 中文名;filename*=UTF-8''... 是 RFC 5987 标准,确保 Chrome/Firefox/Safari 正确解析中文名。
常见问题提醒
-
报错“headers already sent”:检查 PHP 文件开头/结尾是否有空格、BOM 或 echo/print;启用输出缓冲(
ob_start())可缓解 -
下载后文件损坏:确认未在
readfile()前/后有额外输出(包括警告、Notice);禁用错误显示(ini_set('display_errors', 0)) -
大文件卡顿:用
set_time_limit(0)防超时;考虑分块读取(fopen+while+fread+echo)或 Nginx/Apache 的 X-Sendfile 功能(更高效)
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











