php 8.1 文件下载核心是正确设置 content-disposition 和 mime 类型,确保文件存在可读、防止路径遍历、避免输出干扰,并对大文件关闭缓冲、流式生成文件需直写 php://output。

PHP 8.1 实现文件下载到本地,核心是正确设置 HTTP 响应头,让浏览器识别为“要下载的文件”,而不是直接在页面中打开。关键在于 Content-Disposition 头和合适的 MIME 类型,同时需确保文件存在、可读,并避免输出干扰(如空格、BOM、错误提示)。
基础下载:强制浏览器保存文件
适用于已知路径的小到中等大小文件(如 PDF、ZIP、TXT):
<?php $filePath = '/var/www/files/report.pdf';
// 检查文件是否存在且可读
if (!file_exists($filePath) || !is_readable($filePath)) {
http_response_code(404);
die('文件不存在或不可读');
}
// 获取真实文件名(防止路径遍历)
$fileName = basename($filePath);
// 设置响应头
header('Content-Type: application/pdf');
header('Content-Length: ' . filesize($filePath));
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Cache-Control: no-cache');
header('Pragma: no-cache');
// 输出文件内容(不使用 readfile() 时需注意内存)
readfile($filePath);
exit;
?>
安全增强:防止路径遍历与 MIME 探测
用户可能传入恶意文件名(如 ../../etc/passwd),需严格过滤;同时用 mime_content_type() 或 finfo 动态判断类型更可靠:
- 用
basename()提取文件名,丢弃路径部分 - 禁用用户直接传入完整路径,服务端统一管理文件存储目录
- 推荐使用
finfo_open()获取真实 MIME 类型(比后缀更安全)
示例片段:
$safeDir = '/var/www/uploads/';
$userInput = $_GET['file'] ?? '';
$filePath = $safeDir . basename($userInput); // 仅允许同级文件名
if (!str_starts_with(realpath($filePath), $safeDir)) {
http_response_code(403);
die('非法访问');
}
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $filePath) ?: 'application/octet-stream';
finfo_close($finfo);
header('Content-Type: ' . $mimeType);
header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
header('Content-Length: ' . filesize($filePath));
readfile($filePath);
exit;
大文件下载:避免内存溢出
对几百 MB 以上的文件,readfile() 仍安全(它分块输出),但需关闭输出缓冲并禁用压缩:
- 调用
ob_end_clean()清除已有缓冲区 - 加
header('X-Accel-Buffering: no')(Nginx)或header('Cache-Control: no-store') - 避免任何
echo、print或警告输出(开启display_errors=Off)
流式下载(如动态生成 ZIP 或加密文件)
不落地存储,边生成边输出:
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="archive.zip"');
header('Cache-Control: no-cache');
$zip = new ZipArchive();
$zip->open('php://output', ZipArchive::OVERWRITE); // 直接写到输出流
$zip->addFromString('hello.txt', 'Hello from PHP 8.1!');
$zip->close(); // 自动触发下载
exit;
不复杂但容易忽略细节,重点是头信息顺序、无额外输出、路径安全和 MIME 准确性。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











