秒传核心是文件指纹比对而非上传。客户端分片计算并合并哈希生成复合指纹,预检请求服务端查库匹配唯一索引的fingerprint字段;命中则直接返回文件ID,不接收文件流、不写磁盘,实现真正“秒传”。

秒传核心不是上传,而是文件指纹比对
PHP 实现“秒传”,本质是跳过重复文件的物理上传过程。关键在于:客户端提前计算文件的唯一指纹(如 sha1_file() 或 md5_file()),服务端仅校验该指纹是否已存在。如果存在,直接返回已有文件路径或 ID,不走 move_uploaded_file() 流程。
注意:不能依赖前端传来的哈希值做唯一判断——它可能被篡改或伪造。服务端必须在接收到完整文件后重新计算(适用于小文件);但真正秒传场景下,应由客户端分片计算并上传指纹,服务端只查库,不碰原始文件流。
用 sha1_file() 做服务端快速校验的前提和风险
sha1_file() 速度快、碰撞概率低,适合中小文件(
- 若坚持用
sha1_file(),必须配合upload_tmp_dir配置和及时清理:sys_get_temp_dir()路径需有足够空间,且校验后立即unlink()临时文件 - 大文件(>100MB)调用
sha1_file()易超时或内存溢出,PHP 默认max_execution_time=30,需临时设为set_time_limit(0) - Linux 下注意
open_basedir是否限制了对临时目录的访问,否则报错sha1_file(): open_basedir restriction in effect
客户端必须参与的三步:分片哈希 + 合并指纹 + 预检请求
真正的秒传依赖前后端协同。PHP 后端只负责响应预检请求,不做计算:
立即学习“PHP免费学习笔记(深入)”;
- 前端用 Web API(如
FileReader+crypto.subtle.digest())对文件分块计算sha1,再拼接所有块哈希生成“复合指纹”(如sha1(sha1(chunk1).sha1(chunk2)...)) - 上传前发
POST /api/check-upload,携带该指纹字符串(如file_fingerprint字段) - PHP 接口查数据库:
SELECT id, path FROM uploads WHERE fingerprint = ?,命中则直接返回{"code":200,"data":{"exists":true,"file_id":123}}
此时 PHP 不接收 $_FILES,不触发任何文件写入 —— 这才是“秒”的来源。
数据库设计和去重逻辑不能只靠单字段索引
指纹字段(如 fingerprint)必须建唯一索引,但要注意:
- 用
VARCHAR(40)存sha1(40字符十六进制),别用TEXT,否则无法建高效唯一索引 - 同一个文件多次上传,应复用原记录,但需更新
updated_at和引用计数(ref_count),避免误删 - 如果支持不同用户上传同文件,需联合索引:
UNIQUE KEY `user_fingerprint` (`user_id`, `fingerprint`),否则 A 用户秒传会撞 B 用户的文件 - 删除文件时,务必先
UPDATE SET ref_count = ref_count - 1,仅当ref_count = 0才DELETE并unlink($path)
漏掉引用计数,多用户场景下极易出现“文件被删但别人还在用”的静默错误。



















