推荐一次性查出用户全部文件并在内存中构树,而非递归查库;秒传失败主因是md5大小写不一致、大文件oom、断点续传残留;接口需用@requestpart接收md5并校验格式;挂载操作应避免冗余计数字段和先查后插。

Java 实现文件树形结构:用递归还是数据库自关联?
树形结构不一定要靠递归查库,尤其当用户文件量大、层级深时,每次展开节点都查一次 SELECT * FROM files WHERE parent_id = ? 容易拖慢响应。更稳的做法是一次性查出当前用户全部文件,内存中构树。
- 必须给
files表加user_id和parent_id字段,parent_id允许为NULL(根目录) - 查询时按
parent_id排序,能提升后续构树遍历效率;别忘了加索引:CREATE INDEX idx_user_parent ON files(user_id, parent_id) - 避免用
Files.walk()直接扫磁盘——这绕过了权限控制和逻辑删除标记,也和数据库状态不同步
MD5 秒传校验失败的三个高频原因
秒传不是“算完 MD5 就完事”,它依赖服务端已有文件指纹的准确匹配和客户端上传前的可靠预检。常见失败根本不在算法本身,而在流程断点。
- 客户端计算 MD5 前没做分块读取,大文件直接
FileInputStream.readAllBytes()容易 OOM;应使用MessageDigest.update()分批喂入 - 服务端存的 MD5 是小写,但客户端用了
digest.toString().toUpperCase()——大小写不一致导致查不到记录 - 没处理断点续传场景:同一文件多次上传,但中间被中断,服务端残留了不完整记录,却仍返回“已存在”
Spring Boot 中实现秒传接口:Controller 层怎么接参才不踩坑
@RequestParam 不能直接收文件流再算 MD5,因为 Spring 默认会把整个文件读进内存;必须用 @RequestPart 配合 MultipartFile,并手动控制输入流读取节奏。
- 接口参数别写成
@RequestBody String md5——JSON 解析失败率高,改用@RequestPart("md5") String md5更稳定 - 前端传来的 MD5 必须做格式校验:
if (!md5.matches("[a-f0-9]{32}")),防止 SQL 注入或空值穿透 - 不要在 Controller 里直接调 DAO 查 MD5,应封装到 Service 方法里,方便后续加缓存(比如用
@Cacheable(key="#md5"))
文件树 + 秒传联动时,数据库怎么设计才不锁表
用户点击“秒传成功”后要自动挂载到当前目录,这个动作看似简单,实则涉及两次写操作:插入新记录 + 更新父节点的子计数。并发高时容易触发行锁甚至死锁。
立即学习“Java免费学习笔记(深入)”;
-
files表里别冗余存child_count字段——每次增删都要更新父行,冲突概率高;改用实时SELECT COUNT(*)查询(加覆盖索引可接受) - 秒传成功后的“挂载”操作,必须用
INSERT ... SELECT或INSERT IGNORE,避免先查再插导致重复记录 - 如果真要用乐观锁控制并发,版本号字段得加在
files表上,而不是用户表;否则一个用户多设备同时操作仍会冲突
树结构的扁平化加载、MD5 的字节级一致性、接口参数的传输边界、数据库写操作的并发粒度——这四点任一没对齐,秒传就会变成“秒不传”。










