RandomAccessFile 不直接支持断点续传或多线程下载,但通过 seek() 定位写入、避免覆盖、配合 HTTP Range 请求与分块管理,可实现断点续传和多线程下载。

RandomAccessFile 本身不直接支持断点续传或多线程下载,但它提供了关键能力:**随机定位写入、文件指针控制、不覆盖已有内容**。配合 HTTP Range 请求和线程安全的分块管理,就能构建出可靠的断点续传下载逻辑。
1. 断点续传的核心原理与 RandomAccessFile 的作用
断点续传依赖两个前提:
- 服务端支持
Range请求(返回206 Partial Content) - 客户端能记录已下载字节位置,并从指定偏移开始写入文件
RandomAccessFile 在这里承担“精准写入”的角色:通过 seek(offset) 跳转到目标位置,再用 write(byte[]) 写入对应数据块,完全绕过顺序流的限制。它不会清空文件,也不会自动追加(除非你主动 seek 到末尾),所以非常适合补写中间缺失的片段。
2. 多线程分块下载的关键设计
不能让多个线程随意往同一个文件任意位置写——需避免写入重叠或错位。推荐做法是:预先划分固定大小的块(如 1MB),每个线程负责一个独立区间。
立即学习“Java免费学习笔记(深入)”;
本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
- 计算总长度(先发 HEAD 请求获取
Content-Length) - 按线程数 N 拆分为 N 个 [start, end) 区间(注意最后一个块 end = contentLength)
- 每个线程创建自己的
RandomAccessFile(只读模式打开,或共享一个实例但加锁控制 seek+write) - 向服务端发送
Range: bytes=start-end请求,接收响应体后 seek 到 start 再 write
示例片段(单线程写入某一块):
RandomAccessFile raf = new RandomAccessFile("target.zip", "rw");
raf.seek(startOffset); // 定位到本块起始
raf.write(chunkBytes); // 写入本块数据(长度 = end - start)
raf.close();
3. 如何安全记录和恢复断点
断点信息不能只靠文件大小判断(可能写了一半崩溃)。建议用外部元数据文件(如 target.zip.download)记录:
- 总长度、分块数量、每个块的 [start, end, status](status 可为 “done” / “failed” / “pending”)
- 下载前读取该元数据,跳过已完成块,只调度未完成块的线程
- 每块写入成功后原子更新元数据(例如写入新文件再 rename 替换)
注意:RandomAccessFile 没有事务,所以必须靠外部协调保证一致性。不要依赖 raf.length() 做断点判断——它只是当前文件大小,无法反映哪几段真正完整写入。
4. 线程安全与资源管理要点
多个线程共用一个 RandomAccessFile 实例时,seek() 和 write() 不是原子操作,必须同步:
- 最简单方式:每个线程独占一个
RandomAccessFile(打开/关闭开销小,且避免竞争) - 若复用同一实例,需对
raf加锁,且确保seek + write在同一临界区内 - 务必在 finally 或 try-with-resources 中 close,否则文件句柄泄漏会导致后续写入失败
- 写入后可调用
raf.getChannel().force(true)强制刷盘(提高断电恢复可靠性)








