java大文件分片上传用randomaccessfile实现随机写入,核心是服务端统一分配offset、预设文件长度、seek定位后写入,并通过独占锁或单线程执行保障并发安全,最后校验并重命名。

Java 大文件分片上传中,用 RandomAccessFile 实现随机定位写入,核心是:每个分片按服务端指定的偏移量(offset)写入到临时合并文件的对应位置,避免顺序拼接导致的 IO 阻塞和内存压力。
理解分片上传与偏移量对齐
客户端将大文件切为固定大小(如 2MB)的分片,按序号上传。服务端需记录每个分片的起始字节位置(即 offset),例如第 0 片从 0 开始,第 1 片从 2097152 开始。只有 offset 准确,RandomAccessFile 才能跳转到正确位置写入,否则内容错乱或覆盖。
- 服务端必须在接收分片前,校验该分片的 offset 是否与预期一致(可基于分片序号 × 分片大小计算)
- 不依赖客户端传来的 offset,而是由服务端统一计算并返回给前端,防止篡改
- 所有分片最终写入同一个临时文件(如
upload_abc123.tmp),而非各自独立文件
用 RandomAccessFile 安全写入分片
创建 RandomAccessFile 时使用 "rw" 模式,并调用 seek(offset) 定位,再用 write(byte[]) 写入原始字节。关键点在于:文件必须提前分配足够空间,否则 seek 到末尾后 write 会扩展文件,但中间空洞可能引发读取异常(尤其后续用 FileChannel 映射时)。
- 推荐方式:首次写入前,用
setLength(expectedTotalSize)预分配完整文件空间(如 5GB 文件就 setLength(5L * 1024 * 1024 * 1024)) - 每次写入前检查
raf.getFilePointer()是否等于目标 offset,不等则seek(offset) - 写入后建议调用
raf.getChannel().force(true)强制刷盘(尤其对可靠性要求高的场景)
并发写入需加锁或串行化
多个分片可能并发到达,若直接让多个线程同时操作同一个 RandomAccessFile 实例,会出现写错位置、数据覆盖等问题。不能仅靠 synchronized(raf),因为 raf 的内部状态(如 file pointer)不是线程安全的。
- 最稳妥做法:用一个全局唯一文件 ID(如 UUID)作为 key,对每个文件的写入操作加独占锁(如
ConcurrentHashMap.computeIfAbsent(fileId, k -> new ReentrantLock())) - 或者更轻量:所有对该文件的分片写入请求,提交到单线程的
ExecutorService中顺序执行 - 避免使用
FileOutputStream+skip(),它不支持随机写,且 skip 效率极低
合并完成后的校验与清理
当所有分片标记“上传完成”,服务端需确认临时文件大小等于总长度,且每个分片已写入(可通过 Redis 或 DB 记录已上传的分片序号)。之后可重命名为正式文件名,并做基础完整性校验。
- 用
Files.size(path)校验文件总长度是否匹配预期 - 可选:计算整个文件的 MD5 或 SHA-256,与客户端预计算值比对(注意大文件流式计算,避免全量加载内存)
- 成功后删除分片元数据记录,临时文件重命名;失败则清理临时文件,避免磁盘堆积
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











