分片上传核心是“边传边控”,需前后端严格协同:前端按约定大小切片、带元数据上传并查询校验,后端读php://input接收、校验索引、暂存分片、状态记录与加锁原子合并。

分片上传的核心逻辑不是“切完再传”,而是“边传边控”
ThinkPHP 本身不提供开箱即用的分片上传功能,它只负责接收和保存文件。所谓分片上传,本质是前端把文件切块、按序发送,后端逐块接收、校验、暂存,最后合并——整个过程需前后端严格协同,不能依赖框架自动完成。
前端切片必须与后端配置完全对齐
切片大小(chunkSize)不是随便定的。常见设为 2MB 或 5MB,但关键在于:这个值必须和后端代码里用于拼接路径、判断索引、校验总数的逻辑保持一致。一旦前后端 chunkSize 不一致,合并时就会漏块或错序,导致文件损坏。
- 推荐用
file.name + file.lastModified + chunkIndex拼接后取 MD5 生成 chunkId,确保同一文件不同时间上传也产生唯一标识 - 每个分片上传前,先发 GET 请求到
/api/upload/check?chunkId=xxx&chunkIndex=n查询是否已存在;返回 200 就跳过,返回 404 再 POST 上传 - 上传成功后立即写入
localStorage,记录该分片状态(不能用 sessionStorage,刷新即丢)
后端接收分片要绕过 request()->file()
WebUploader 或原生 File API 分片上传时,发送的是原始二进制流(raw body),不是 multipart/form-data。此时 request()->file() 必然为空,强行使用会直接失败。
- 正确做法是读取
php://input:$data = file_get_contents('php://input'); - 结合参数
input('chunk')、input('chunks')、input('name')构建临时路径,例如runtime/chunks/{date}/{chunkId}/{chunkId}-{chunkIndex}.tmp - 务必校验
chunk是否为非负整数,防止路径穿越(如传入../../../etc/passwd) - 所有分片统一存入带日期子目录,避免单目录文件过多影响 Linux 文件系统性能
合并必须加锁 + 校验 + 原子操作
收到最后一块(chunk == chunks - 1)不等于可以立刻合并。真实场景中可能有并发请求、网络重试、重复提交,必须靠状态表和文件锁兜底。
- 建表
upload_status,字段含chunkid、total_chunks、uploaded_count、status、updated_time - 每次成功写入一个分片,就执行
INSERT ... ON DUPLICATE KEY UPDATE uploaded_count = uploaded_count + 1 - 合并前先查表确认
uploaded_count == total_chunks,再用flock()锁住目标文件,校验所有分片 MD5 合并后整体 MD5,最后才 rename 成正式文件 - 合并失败时保留分片、回滚状态,前端可继续断点续传,而不是清空重来
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











