RandomAccessFile合并分片需确保文件存在、按chunkIndex×chunkSize精准seek写入、避免共享实例;前端须用FormData传chunkIndex/identifier;断点续传靠查询已存分片索引;合并时用文件锁或Redis锁防并发,完成后清空临时目录。

Spring Boot里怎么用RandomAccessFile合并分片文件
分片上传后必须把所有chunk按序写入目标文件,RandomAccessFile是少数能精准定位偏移量、避免重复读写的方案。但直接seek()再write()容易出错——比如文件没提前创建、多线程并发写同一RandomAccessFile实例、或未对齐分片序号与字节偏移。
实操建议:
- 先用
new File(targetPath).createNewFile()确保目标文件存在且为空,否则RandomAccessFile以"rw"模式打开时可能截断已有内容 - 每个分片的写入位置 =
chunkIndex * chunkSize(注意:服务端必须严格校验chunkIndex从0开始、连续、无跳变) - 不要在多个线程间共享同一个
RandomAccessFile对象;每个写操作应独立打开→定位→写入→关闭,或用FileChannel配合position()更安全 - 合并前校验所有分片MD5或SHA-256,防止客户端传错块却成功写入
前端传来的分片信息怎么和后端存储对齐
常见错误是后端用HttpServletRequest原始流解析分片参数,结果拿不到chunkIndex、totalChunks、identifier等关键字段,导致无法判断该写哪、是否最后一片、甚至混淆不同文件的分片。
实操建议:
- 强制前端通过
FormData提交,且必须包含三个必填字段:file(Blob)、chunkIndex(数字)、identifier(唯一文件ID,如MD5(file.name + timestamp)) - 后端用
@RequestParam("chunkIndex") Integer chunkIndex而非从body流手动解析,避免编码/边界问题 - 把
identifier作为临时目录名,例如/upload/chunks/{identifier}/,避免不同用户上传同名文件时分片覆盖 -
totalChunks只用于校验完整性,不要依赖它决定是否触发合并——真正可靠的是收到chunkIndex == totalChunks - 1且本地已存满totalChunks个文件
断点续传怎么判断“该从哪继续”
断点续传不是靠后端记住“上次传到第几片”,而是前端主动问:“我这个文件(identifier)已经传了哪些片?” 后端查目录+返回已存在分片索引列表,前端据此跳过重传。
实操建议:
- 提供一个
GET /api/upload/status?identifier={id}接口,返回List<integer></integer>,内容是当前/chunks/{id}/下所有已存分片的chunkIndex - 不要在数据库存“已传多少片”,直接
new File(chunkDir).listFiles()并解析文件名中的序号——简单、一致、无状态 - 前端拿到索引列表后,只上传缺失的
chunkIndex,且需校验每个分片的Content-MD5请求头,防止本地缓存损坏仍被跳过 - 注意NFS或分布式文件系统下
listFiles()可能有延迟,生产环境建议加本地缓存(如Caffeine),过期时间设为30秒
合并完成后怎么防止重复触发或残留临时文件
最常踩的坑是:多个请求同时检测到“最后一片已到”,并发执行合并逻辑,导致文件损坏或IOException: The process cannot access the file(Windows下尤其明显)。
实操建议:
- 用文件锁(
FileChannel.lock())或Redis分布式锁控制合并入口,锁key为"merge_lock:" + identifier,超时设为120秒 - 合并成功后立即删除整个
/chunks/{identifier}/目录,而不是单个文件——避免下次上传同identifier时误复用旧分片 - 加一层“合并任务队列”,比如用
ConcurrentHashMap<string completablefuture>></string>缓存正在合并的identifier,后续请求直接join()等待,不重复提交 - 务必在
finally块中释放锁、关闭流、清理临时资源,哪怕合并抛异常也要保证目录可删
真正的难点不在RandomAccessFile本身,而在于分片元数据的一致性、并发控制粒度、以及前后端对“完成”的定义是否完全同步。漏掉任意一环,断点续传就变成“断点重传”。

















