thinkphp 6.0超大附件上传服务支持断点续传、分片上传、多并发及刷新不丢进度:前端按2mb切片并生成唯一chunkid,上传前校验存在性,成功后持久化至localstorage;后端用runtime/chunks分目录存储,upload_status表跟踪状态,合并时加锁校验md5确保原子性。

你需要在ThinkPHP 6.0项目中搭建一套能真正应对生产环境的超大附件上传服务,要求支持断点续传、分片上传、多文件并发、刷新页面后进度不丢——不是Demo级代码,而是能扛住10GB视频、5万个小文件、IE11+Chrome/Firefox混合访问的真实系统。
前端分片与上传状态持久化
第一步:用File API读取文件并按2MB切片,注意chunkSize必须与后端配置严格一致,否则合并时校验失败。
第二步:对每个分片生成唯一chunkId(推荐用file.name + file.lastModified + sliceIndex拼接MD5),这个ID将贯穿整个上传生命周期,【不可重复、不可随机生成】。
第三步:每次上传前先发GET请求到/api/upload/check?chunkId=xxx&chunkIndex=0,询问该分片是否已存在。服务端返回200表示已存,跳过上传;返回404才执行POST上传。这一步省掉重复传输,也避免断点恢复时误重传。
第四步:上传成功后立即调用localStorage.setItem('upload_' + chunkId, JSON.stringify({index: i, status: 'success', time: Date.now()}))。别用sessionStorage——它在刷新后清空,断点续传就失效了。
TP6控制器接收与临时存储
方法一:使用request()->file()直接接收单个分片
在app/controller/Upload.php中定义uploadChunk方法,接收chunkid、chunkindex、chunkcount、filename参数;【务必校验chunkindex是否为数字且≥0,防止路径穿越攻击】。
方法二:手动读取原始流避免TP6自动解析干扰
当遇到超大分片或Nginx限制时,改用file_get_contents('php://input')获取原始二进制流,再用file_put_contents写入临时目录。这种方式绕过TP6文件校验,但需自行做MIME类型白名单过滤。
所有分片统一存到runtime/upload/chunks/{date}/{chunkid}/目录下,文件名格式为{chunkid}-{chunkindex}.tmp。日期子目录可防止单目录文件过多导致Linux ext4性能下降。
服务端断点状态管理
第一步:创建upload_status表,字段含id、chunkid、total_chunks、uploaded_count、status('uploading'/'completed'/'failed')、updated_time。
第二步:每次分片上传成功,执行INSERT ... ON DUPLICATE KEY UPDATE语句更新uploaded_count,同时记录updated_time。主键设为chunkid,避免重复插入。
第三步:用户刷新页面后,前端调用/api/upload/status?chunkid=xxx,后端查upload_status表返回当前已上传分片索引数组,前端据此跳过已传分片,从第一个missing index继续。
第四步:若某分片连续3次上传失败(HTTP 500或超时),status置为'failed',并记录错误码。前端收到后弹窗提示“第X块上传失败,请检查网络”,不自动重试——避免死循环刷爆磁盘。
合并逻辑与原子性保障
当最后一个分片(chunkindex == chunkcount - 1)上传完成,触发合并流程:
① 先用flock加写锁,防止多个请求同时合并同一文件;
② 遍历检查所有分片文件是否存在且大小正确(每个应≈2MB,最后一片可能略小);
③ 按chunkindex升序逐个fopen→fread→fwrite追加到目标文件;
④ 合并完成后计算最终文件MD5,与前端传来的fileMd5比对,不一致则unlink目标文件并报错;
⑤ 成功后清理所有.tmp分片文件,并将upload_status.status更新为'completed'。











