php文件下载核心是正确设置http响应头强制浏览器下载,需校验权限、净化文件名、限制后缀白名单,并用readfile或框架response()->download安全输出,大文件须支持range断点续传。

PHP 8.0 实现文件下载到本地,核心是正确构造 HTTP 响应,让浏览器识别为“附件”并触发保存动作。关键不在于功能能否实现,而在于是否安全、兼容、稳定——尤其面对中文名、大文件、权限控制等真实场景。
用内置函数最稳妥:header + readfile(基础但需谨慎)
这是原生 PHP 最常用方式,适合小文件(
- 先设置响应头:
header('Content-Type: application/octet-stream');告诉浏览器这是二进制流 - 用
basename($filename)提取纯文件名,防止路径穿越攻击(如../../etc/passwd) - 中文文件名推荐用 RFC 5987 编码:
header("Content-Disposition: attachment; filename*=UTF-8''" . rawurlencode($zhName)); - 调用
readfile($absPath)输出内容后,务必加exit;防止后续输出污染响应体
ThinkPHP 8.0 推荐方案:response()->download()
如果你用的是 ThinkPHP 8.0,直接用框架封装的方法更省心也更安全:
- 必须传入**绝对路径**,且该文件不能放在
public/这类 Web 可直访目录下(否则绕过权限) - 中文名无需手动编码,TP8 内部自动按 RFC 5987 处理:
response()->download($path, '订单_202609.xlsx'); - 它默认启用分块输出(
fpassthru+ 缓冲控制),避免内存溢出;同时校验文件可读性 - 注意:调用后需返回 Response 对象(如
return response()->download(...);),不能继续执行后续逻辑
大文件或需断点续传?得自己处理 Range 请求
超过 100MB 或要支持下载暂停/续传时,response()->download() 不够用,需手动实现 206 Partial Content:
- 解析
$_SERVER['HTTP_RANGE']获取字节范围(如bytes=0-1023) - 用
fopen($path, 'rb')打开文件,fseek()跳转到起始位置 - 设置
header('HTTP/1.1 206 Partial Content')和Content-Range - 循环
fread($fp, 8192)+echo+flush(),并在开头清空所有输出缓冲:while (ob_get_level()) ob_end_clean();
千万别跳过的三道防线
无论用哪种方式,漏掉以下任一环节都可能引发越权或安全漏洞:
- 业务权限校验:查数据库确认当前用户是否有权下载该文件(不能只靠 ID 参数)
-
文件名净化:始终用
basename()剥离路径,再结合pathinfo($name, PATHINFO_EXTENSION)提取后缀 -
后缀白名单:只允许
['pdf', 'xlsx', 'zip', 'csv']等业务必需类型,拒绝.php、.log等危险扩展名
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











