跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

Laravel中怎么实现文件下载_Laravel response:download用法【方案】

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

相关文章