秒传功能需前端用Web Worker异步计算文件hash(如SHA-1),首片即上传identify;ThinkPHP后端在moveTo前查库比对hash,命中则直接返回已有URL,跳过落盘,且hash字段须建B-Tree索引。

秒传功能在 ThinkPHP 里不是开箱即用的,必须手动介入文件上传流程,在保存前加一层 hash 查询和复用逻辑。核心就一条:别让相同内容的文件写两次磁盘。
前端怎么传文件 hash 才不卡死页面
大文件(比如 500MB+)直接用 FileReader 同步算 md5 会阻塞主线程,用户操作完全冻结。不能依赖 spark-md5 的同步接口,得用异步方式。
- 优先用
Web Worker+spark-md5的hashBinary分块计算,把耗时操作移出主线程 - 若不支持 Worker(如某些旧版 iOS),改用
requestIdleCallback切片计算,每次只处理几 MB,留出响应时间 - 别等整个文件读完再发请求——首片上传时就把
identify(即文件完整 hash)作为参数带上,后端靠它查库 - 注意:
sha1_file()和md5_file()是 PHP 函数,只能在服务端用;前端必须自己算,不能偷懒交由后端读文件再算
ThinkPHP 后端怎么查 hash 并跳过上传
ThinkPHP 自带的 UploadFile 类或 $request->file() 不管 hash,你得在它调用 moveTo() 前插一手。关键点是:查到已有记录,就得阻止实际落盘。
- 接收上传前,先从请求中取
identify参数(比如前端传的sha1或md5值) - 用
Db::name('files')->where('hash', $identify)->find()查是否存在同 hash 记录 - 如果存在,直接返回已有
url,并确保不执行$file->moveTo()——否则会重复保存,还可能触发unlink()权限错误 - 特别注意:
$file->hash('sha1')是 ThinkPHP 5.1+ 提供的快捷方法,但它会重新读一遍文件,大文件很慢;不如前端传过来的 hash 可信,直接用即可 - 数据库字段
hash必须建 B-Tree 索引,不然查 10 万条记录要几百毫秒
为什么 file_exists() 不能代替 hash 查询
file_exists() 检的是路径存在性,不是内容一致性。两个不同文件可能被保存为同名(比如都叫 report.pdf),但内容完全不同。秒传要防的是“内容重复”,不是“文件名重复”。
立即学习“PHP免费学习笔记(深入)”;
-
file_exists('./public/uploads/xxx.pdf')返回 true,只说明这个路径有文件,不代表它是你要的那个版本 - 有人想用
sha1_file()在服务端重算再比对,但大文件会卡住 PHP 进程,且浪费 I/O;前端已算好,直接信它 - 真正该用
file_exists()的地方,是合并分片后检查目标文件是否生成成功,或清理临时目录前确认路径可访问 - 权限上,
file_exists()成功不代表is_readable()成功,生产环境务必组合判断
分片上传下如何保持秒传逻辑一致
分片场景下,秒传不是“跳过整个上传”,而是“跳过所有分片写入 + 合并”。只要首片请求带了正确的 identify,后端就能提前知道该文件已存在。
- 首片请求到达时,查
identify→ 若命中,直接返回{"exist": true, "url": "/uploads/xxx.jpg"},前端不再发后续分片 - 别在每片都查一次数据库,首片查过就够了;否则并发高时 DB 压力陡增
- 分片临时目录(如
runtime/upload/chunks/{identify}/)必须和主文件存储隔离,避免秒传时误删正在上传的分片 - 合并完成后,记得清空对应
chunks/{identify}/目录——漏清会导致磁盘悄悄占满,且下次同 hash 上传可能误读残留分片
最容易被忽略的是:秒传成功后,前端拿到的 url 必须和普通上传路径格式完全一致,否则业务层要写两套资源解析逻辑。路径规则、域名、CDN 配置,全得对齐。



















