直接用 response()->download() 触发下载需确保:文件路径为服务器绝对路径(如 storage_path())、中文名用 RFC 5987 编码、大文件改用 streamDownload 流式输出、严格校验路径防越权。
直接用就能触发浏览器下载,但文件名乱码、路径错误、大文件卡死、流式传输失效——这些问题全出在参数和上下文处理上。
文件路径必须是服务器本地绝对路径
传给
的第一个参数不能是 URL 或相对路径,必须是 PHP 能读取的绝对路径,比如
。Laravel 的
和
是最安全的起点。
✅ 正确:
❌ 错误:
(会报“File does not exist”)
❌ 错误:
(PHP 找不到该相对路径)
中文文件名要手动设置 Content-Disposition
默认情况下,
对中文文件名只做原始 ASCII 编码,Chrome/Firefox 会显示乱码或截断。必须显式传入第二个参数(
),并用
或 RFC 5987 格式编码。
兼容性最稳写法:
更标准做法(推荐):
注意:不要同时用两个 header,否则可能被覆盖
大文件别直接 load 到内存,用 streamDownload 替代
当文件超过几十 MB,
会把整个文件读进内存再吐出去,容易触发
。此时应改用
,配合
+
流式输出。
示例:
优势:内存占用恒定,适合 GB 级日志、导出包
注意:Nginx 默认有
,可能缓存整个响应;需加
关闭缓冲
权限与符号链接要提前校验
Laravel 不检查文件是否可读、是否越界、是否为软链指向系统目录。如果用户能构造路径参数(如通过 URL 传
),就存在任意文件读取风险。
务必用
或
+
+
校验路径合法性
禁止让用户输入直接拼进路径;如需动态文件,用 ID 查数据库映射真实路径
避免使用
下的用户上传内容作下载源,优先走
+ 中间件鉴权
真正麻烦的不是调用那行代码,而是路径来源是否可信、文件名编码是否被客户端正确解析、以及大文件时 Web 服务器有没有偷偷缓存或截断流——这些地方一漏,下载就静默失败或变下载 HTML 页面。
response()->download()response()->download()/var/www/storage/app/private/report.pdfstorage_path()public_path()response()->download(storage_path('app/reports/2024-invoice.pdf'))response()->download('https://example.com/file.pdf')response()->download('storage/app/reports/xxx.pdf')response()->download()$filenamemb_convert_encoding()response()->download($path, mb_convert_encoding($name, 'UTF-8', 'auto'))response()->download($path, $name)->header('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($name))response()->download()Allowed memory size exhaustedresponse()->streamDownload()fopen()fpassthru()response()->streamDownload(function () use ($path) {
$file = fopen($path, 'rb');
fpassthru($file);
fclose($file);
}, $name);fastcgi_buffering on->header('X-Accel-Buffering', 'no')filename=../../../etc/passwdStorage::exists()file_exists()is_file()realpath()public_path()storage_path()