无论是多线程下载,还是断点续传等复杂的功能,都需要先了解原理,遇到的问题。下面上图介绍一下。
逐个介绍一下。
http相关字段 :
1、Transfer-Encoding:chunked: 这个字段很重要,从字面意义上讲这是客户端接收传输的编码方式。这种编码方式的意思是,我们前段在展示一个比较大的内容的时候,服务器不是一次性把数据传过来的(一次性比较耗时),而是一块一块的传过来,这时候采用的就是“Transfer-Encoding:chunked”编码方式,而这样会有一个问题,就是在http响应头里面就会拿不到这个文件的总长度。“content-length”返回值是 -1。后面我会贴上代码,和解决方法。
2、content-length : 作用在响应头中的数据。它可以拿到这个文件的总长度,这个就可以多线程分配每个线程下载多少。 这个字段很重要。 而如果此时数据是“Transfer-Encoding:chunked:” 那么会拿不到文件的总长度,返回值是 -1。 一般静态的文本文件、图片等都是可以拿到的,最终能不能拿到取决于服务器
3、Range : 它可以访问服务器中某一个数据制定长度的内容。比如我们在多线程下载的时候,已经下载了一部分保存到本地了,然后我们第二次打开重新下载的时候,我们就可以通过Range指定可以从多少个字节接着下载。
下面给大家介绍了解一下多线程下载必须得到参数”文件的总长度 content-length“。
1、访问服务器,获取不到 ”文件的总长度 content-length“ 案例
访问www.qq.com ,它就是Transfer-Encoding:chunked 编码方式,一块一块传输的,不能文件总长度,代码如下,看一下。
public static void main(String args[]) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("http://www.qq.com").
build();
try {
Response response = client.newCall(request).execute();
// 此处返回-1,说明qq网站是 Transfer-Encoding:chunked,所以取不到文件长度
System.out.println("content-length : "+response.body().contentLength());
if (response.isSuccessful()) {
Headers headers = response.headers();
// 遍历响应头信息
for (int i = 0; i < headers.size(); i++) {
System.out.println(headers.name(i) + " : " + headers.value(i));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
这是响应头打印出来的日志:
content-length : -1 //长度为 -1
Server : squid/3.5.24
Date : Fri, 02 Feb 2018 09:35:33 GMT
Content-Type : text/html; charset=GB2312
Transfer-Encoding : chunked
Connection : keep-alive
Vary : Accept-Encoding
Expires : Fri, 02 Feb 2018 09:36:33 GMT
Cache-Control : max-age=60
Vary : Accept-Encoding
Vary : Accept-Encoding
2、访问一个图片可以返回长度的案例:
public static void main(String args[]) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("https://cpro.baidustatic.com/cpro/ui/noexpire/img/4.0.0/pc_ads.1x.png").
build();
try {
Response response = client.newCall(request).execute();
// 此处文件的长处
System.out.println("content-length : "+response.body().contentLength());
if (response.isSuccessful()) {
Headers headers = response.headers();
// 遍历响应头信息
for (int i = 0; i < headers.size(); i++) {
System.out.println(headers.name(i) + " : " + headers.value(i));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
这是响应头打印出来的日志:
content-length : 611 //长度为611
Server : bfe/1.0.8.13-sslpool-patch
Date : Fri, 02 Feb 2018 12:47:08 GMT
Content-Type : image/png
Content-Length : 611
Connection : keep-alive
ETag : "5a16921e-263"
Last-Modified : Thu, 23 Nov 2017 09:17:18 GMT
Expires : Thu, 25 Nov 2027 07:02:26 GMT
3、指定下载文件的位置案例:
添加请求头,通过“Range”指定下载位置
public static void main(String args[]) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("https://cpro.baidustatic.com/cpro/ui/noexpire/img/4.0.0/pc_ads.1x.png").
addHeader("Range", "bytes=0-3"). // 指定下载0 到第3个字节(第一个位置从0开始)
build();
try {
Response response = client.newCall(request).execute();
// 此处返回文件的长度
System.out.println("content-length : "+response.body().contentLength());
if (response.isSuccessful()) {
Headers headers = response.headers();
// 遍历响应头信息
for (int i = 0; i < headers.size(); i++) {
System.out.println(headers.name(i) + " : " + headers.value(i));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
content-length : 4 // 指定的0 - 3, 下载长度为4
Server : bfe/1.0.8.13-sslpool-patch
Date : Fri, 02 Feb 2018 13:32:21 GMT
Content-Type : image/png
Content-Length : 4
Connection : keep-alive
ETag : "58d25413-263"
Last-Modified : Wed, 22 Mar 2017 10:38:11 GMT
Expires : Sun, 21 Mar 2027 09:25:14 GMT
Age : 27317227
Cache-Control : max-age=315360000
Content-Range : bytes 0-3/611 //此时会多一个字段。 意思就是下载了0 - 3的位置, 一共是 611长度
以上已经给大家介绍的很清楚了,多线程需要了解必备的知识。响应头里面相关的数据。下一篇我会用代码实现给大家解释清楚。
总结并不是所有的响应头返回 “content-length”长度, 一般一些MP3 、静态文件、图片等是有的。
所以想要实现多线程下载、上传文件:
1、请求的响应头必须返回文件的长度。 (content-length)
2、根据总的长度分配每个线程下载多少 (Range)
3、可以查看自己下载的进度 (Content-Range)
还有一点要注意:
如果我们文件下载了一部分突然停掉,或者没网了。然后连接上网以后接着下载,:
我们在下载文件的时候可以通过“Content-Range”字段来知道我们下载的进度。 所以来网以后,我们可以继续请求下载的接口,从而获得下载的进度,然后根据进度从新下载。
如果我们是文件上传,那么也可以通过后台把我们上传文件的进度给返回回来,从而进行操作。