断点下载/续传
断点下载是针对下载大文件需求的一种优化机制,可以从上次下载的断点处继续下载。断点续传原理也相同,只不过一个是客户端主动控制,一个是服务端主动控制。
断点下载实现方式
该方案有如下两个关键点:
- 如何与服务端制定沟通的协议,客户端需要将当前已下载的节点值告知服务端,服务端才可以将剩余的文件内容传输给客户端。
- 另外还有一点是,如果服务端的文件内容已经更改,则需要客户端重新全量的下载。
那么以上这两点我们都可以通过HTTP协议的请求头方式来实现。
1. 告知服务器当前的下载进度
复用HTTP协议请求头的Range字段,该字段支持指定需要的下载范围,例如:
Range: bytes=0-499 下载第0-499字节范围的内容
Range: bytes=500-999 下载第500-999字节范围的内容
Range: bytes=-500 下载最后500字节的内容
Range: bytes=500- 下载从第500字节开始到文件结束部分的内容,项目中可以使用这种范围书写方式(获取当前位置开始还未下载的所有字节数据)
那么如何来实现一个非线性随机的数据加载呢?这时候我们需要使用Java提供的一个功能很强大的随机存取文件类RandomAccessFile,可以实现对文件的随机读写操作。
主要API介绍
- getFilePointer 返回当前的文件指针,即从文件开头算起的绝对位置。
- seek(long pos) 将文件指针定位到指定位置
- length 返回文件长度
- skipBytes(int n) 从当前位置开始跳过n个字节
读操作
Method | 功能描述 |
---|---|
readBoolean | 读布尔类型 |
readChar | 读字符型 |
readInt | 读整型 |
readLong | 读长整形 |
readFloat | 读单精度浮点型 |
readDouble | 读双精度浮点型 |
readLine | 读一行 |
readUTF | 读出使用modified UTF-8编码的字符串 |
写操作
Method | 功能描述 |
---|---|
writeBoolean | 写布尔类型 |
writeChar | 写字符型 |
writeInt | 写整型 |
writeLong | 写长整形 |
writeFloat | 写单精度浮点型 |
writeDouble | 写双精度浮点型 |
writeLine | 写一行 |
writeUTF | 写入modified UTF-8编码的字符串 |
2. 获取服务端文件状态
复用HTTP协议请求头的ETag字段(属于HTTP 1.1属性),服务端提取文件的指纹(一般为资源实体的哈希值)
server端: 计算出文件的指纹(例如:0cc25a818bfbc3de3f02cc98c),赋值给ETag字段
client端:
第一次请求时读取etag值并缓存:
String etag = httpURLConnection.getHeaderField("ETag");
ETag: "0cc25a818bfbc3de3f02cc98c"
第二次请求通过If-None-Match传递etag值到服务端(断点续传时):
httpURLConnection.setRequestProperty(“If-None-Match”, "0cc25a818bfbc3de3f02cc98c");
If-None-Match: "0cc25a818bfbc3de3f02cc98c"
服务器会比对这个客服端发送过来的Etag是否与服务器的相同:
- 如果相同,就将If-None-Match的值设为false,返回状态为304,客户端继续使用本地缓存,不解析服务器返回的数据;
- 如果不相同,就将If-None-Match的值设为true,返回状态为200,客户端重新解析服务器返回的数据。
即ETag就是服务器生成的一个标记,Etag可以是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,用来标识返回值是否有变化,且Etag的优先级高于Last-Modified。
HTTP响应码3xx段 | resultcode msg |
---|---|
301 | Move Permanently |
302 | Found |
304 | Not Modified |
3. 分片下载
分片下载是指同时开启多个线程分别下载一个大文件的各个指定部分(通常每个线程分配固定等额的下载字节量),然后融合成一个文件,大大提升下载的效率,原理如上描述类似。
未完待续,下篇实现一个基于Okhttp的分片下载