Laravel怎么处理大文件上传_Laravel分片上传与断点续传实现【实战】

2次阅读

大文件上传失败因PHP的post_max_size和upload_max_filesize限制,需分片上传:前端切片并发上传并校验,后端用getContent()存分片、redis计数,合并交由队列异步完成。

Laravel怎么处理大文件上传_Laravel分片上传与断点续传实现【实战】

为什么直接用 request()->file() 会失败

大文件(比如 >100MB)上传时,laravel 默认的表单提交方式会卡在 PHP 层就失败:不是 413 Request Entity Too Large,就是 500 且日志里出现 PHP Warning: POST Content-Length of XXX bytes exceeds the limit。根本原因不是 Laravel 拦着你,而是 PHP 的 post_max_sizeupload_max_filesize 限制了整个请求体大小。分片上传必须绕过这个限制——把一个大文件切成小块(如 5MB/片),每片走独立 http 请求,服务端不拼接成完整文件,而是存为临时分片。

前端怎么切片并控制并发

File.prototype.slice()(现代浏览器)或 Blob.slice() 切片,配合 FormData 发送每一片。关键点不是“能发”,而是“怎么发得稳”:

  • 每片带上唯一 identifier(如文件名 + hash)、filenamechunkNumbertotalChunkschunkSize
  • promise.allSettled 控制并发数(比如最多 3 片同时上传),避免压垮 nginx 或触发连接超时
  • 上传前先发个 HEAD 请求查服务端是否已有该分片(用于断点续传)
  • 后端响应必须返回明确状态,例如 { "uploaded": true, "chunk": 5 },不能只返回 200

后端如何接收分片并校验完整性

Laravel 路由接收分片时,不要用 $request->file('file'),它会尝试解析整个 multipart body,容易爆内存。改用 $request->getContent() 原始流读取,并手动写入磁盘:

// 示例:存储分片到 storage/app/chunks/{identifier}/{chunkNumber} $identifier = $request->input('identifier'); $chunkNumber = (int) $request->input('chunkNumber'); $filePath = storage_path("app/chunks/{$identifier}/{$chunkNumber}");  file_put_contents($filePath, $request->getContent());  // 同时记录已上传分片数(可用 Redis incr) Redis::incr("upload:{$identifier}:uploaded_chunks");

合并前必须校验:检查 uploaded_chunks == totalChunks,再遍历所有分片文件按序 cat 进一个目标文件。别忘了用 sha256_file() 校验最终文件哈希是否和前端传来的 fileHash 一致。

断点续传依赖的两个隐藏细节

断点续传不是“前端记着传到哪”,而是前后端共同维护状态。最容易被忽略的是:

  • Nginx 默认关闭 client_body_timeoutclient_header_timeout,大文件上传中途断开后,客户端重试时可能因超时被直接拒绝,需调大(如设为 300s)
  • 分片文件名不能只靠 chunkNumber,否则多个用户上传同名文件会冲突;必须用 md5(原始文件名 . 时间戳 . 随机字符串) 生成 identifier,且该值要从第一次上传就固定下来

合并操作本身不能放在 Web 请求里做——1GB 文件拼接可能耗时几十秒,Nginx 或 PHP-FPM 会杀掉长请求。要用 dispatch(new MergeUploadJob($identifier)) 丢进队列异步处理,完成后通知前端或写入数据库标记完成。

text=ZqhQzanResources