Laravel怎么处理文件下载_Laravel响应文件流教程【传输】

1次阅读

应使用 response()->stream() 实现流式下载,避免 response()->download() 或 storage::download() 导致内存溢出;需手动设置响应头、关闭缓冲、分块读取并 flush;注意 web 服务器(如 nginx)配置禁用缓冲。

Laravel怎么处理文件下载_Laravel响应文件流教程【传输】

response()->stream() 实现可控的文件流下载

直接返回大文件时,别用 response()->download(),它会把整个文件读进内存再吐出去,100MB 的 excel 就可能 OOM。真正要“流式传输”,得自己接管输出缓冲和分块读取逻辑。

关键点在于:手动设置响应头、关闭 laravel 的自动缓冲、按 chunk 读取并 flush 到客户端。

  • ob_end_clean() 必须调用,否则中间件或视图引擎残留的输出缓冲会导致 headers already sent 错误
  • 每次 fread() 后立即 echo + flush(),但要注意 PHP-FPM 默认禁用 fastcgi_finish_request 级别的 flush,Nginx 配置里得关掉 fastcgi_buffering off
  • 务必设置 Content-Transfer-Encoding: binaryX-Accel-Buffering: no(Nginx)或 Cache-Control: no-cache,不然代理或浏览器可能缓存/截断流
return response()->stream(function () {     $file = fopen('/path/to/big.log', 'rb');     while (!feof($file)) {         echo fread($file, 8192);         flush();         usleep(1000); // 防止压垮连接     }     fclose($file); }, 200, [     'Content-Type' => 'application/octet-stream',     'Content-Disposition' => 'attachment; filename="big.log"',     'X-Accel-Buffering' => 'no', ]);

Laravel 中 Storage::download() 的隐藏限制

这个方法看着省事,但它底层调用的是 response()->download(),意味着文件必须先被 readfile()file_get_contents() 加载——对本地磁盘还行,对 S3 或 SFTP 这类远程驱动就危险了。

比如你用 S3 驱动调 Storage::disk('s3')->download('report.pdf'),Laravel 会先把整个 PDF 从 S3 拉到临时目录再传给浏览器,既慢又占磁盘空间。

  • 仅适用于小文件(local 的场景
  • 不支持自定义 HTTP 头(如 Content-Range),无法做断点续传
  • 如果用了 url() 生成预签名链接,别再套一层 download(),那只是重定向,不是流式响应

如何安全地响应远程文件(S3 / FTP)而不落地

核心思路:别让 Laravel 做中转,让它当“跳板”,把客户端引向真实资源地址,或用 stream_wrapper 按需拉取。

  • 对公有 S3 文件,直接返回 302 重定向到预签名 URL:return redirect()->away($s3Client->createPresignedRequest(...)->getUri())
  • 对私有文件且必须走后端鉴权,用 Storage::disk('s3')->readStream($path) 获取 Resource,再喂给 response()->stream(),避免全量加载
  • 注意 readStream() 在某些旧版 Flysystem 驱动里不支持 seek,所以没法做 Range 请求;若需支持断点续传,得自己实现 Content-Range 解析和分段读取

浏览器下载失败?检查这三处 headers 和连接状态

常见现象是进度条卡住、文件大小为 0、或者提示“网络错误”,往往不是代码问题,而是 headers 冲突或连接提前中断。

  • 确保没其他中间件(比如 CORS 中间件)重复设置 Content-Length,流式响应不该设这个头
  • PHP 超时设置(max_execution_time)要大于文件传输预期耗时,否则脚本中断导致连接关闭
  • 如果用 Nginx,确认 proxy_buffering offproxy_max_temp_file_size 0,否则它会把流攒成大块再发,破坏实时性

流式响应不是“写完就能跑”,每个环节都可能吃掉 chunk 或丢掉 flush —— 最容易被忽略的是 Web 服务器层的缓冲策略,它比 PHP 代码更常成为瓶颈。

text=ZqhQzanResources