Android--通过Http协议实现多线程下载
笔记摘要:
主要介绍多线程的下载的实现原理,每段线程从不同位置进行文件的下载,其中对于文件的分段下载,主要使用到了随机读写文件类:RandomAccessFile,
这里通过示例进行演示
一、多线程下载原理
1、得到网络文件的长度,然后在本地生成一个与它长度相等的本地文件,
2、需要计算每天线程的数据下载量,公式如下:
假设使用N条线程下载,文件的长度为length,
int block = 文件长度%N==0 ? 文件长度/N : 文件长度/N+1
3、开启多天线程分别从网络文件的不同位置下载数据,并从本地相同位置写入数据,所以要计算出每条线程从网络文件的什么位置开始下载数据,到什么位置结束。
起始位置:int start = threadid*block
结束位置:int end = (threadid+1)*block-1
当所有线程下载完成后,文件下载成功。
这里需要进行两类请求:
1、对需要下载文件时进行请求
2、每个线程进行下载时都要对自己所要下载的区域进行请求
注意:多线程下载请求成功的状态码是206
二、代码实现:
出现的问题:
在DownloadThread中读取时,使用了read,而不是read(buf),导致连接时间超时或连接重置
创建一个web服务,并提供一个文件用于下载,这里创建的是名为web的服务器,并提供“foobar.exe”下载
多线程下载的业务类
import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; public class MulThreadDownload { public static void main(String[] args) { String path = "http://10.1.8.137:8080/web/foobar.exe"; try { new MulThreadDownload().download(path, 3); } catch (IOException e) { e.printStackTrace(); } } private void download(String path,int threadsize) throws IOException { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(50000); conn.setRequestMethod("GET"); System.out.println("donwload....."); if(conn.getResponseCode()==200){ int length = conn.getContentLength();//获取文件的长度 File file = new File(getFilename(path)); //元数据:时间、作者等 //为避免数据的丢失,以读写方式打开文件,模式为:”rwd",当调用write方法时,对每个更新都写入到底层存储设备 RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");//在本地生成一个长度相等的文件 accessFile.setLength(length); accessFile.close(); //计算每条线程负责下载的数据量 int block = length%threadsize==0 ? length/threadsize : length/threadsize+1; for(int threadid=0;threadid<threadsize;threadid++){ new DownloadThread(threadid,block,url,file).start(); } }else{ System.out.println("下载失败!"); } } private String getFilename(String path) { return path.substring(path.lastIndexOf("/")+1); } }
多线程下载线程类:实现每个线程下载指定区域的文件
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class DownloadThread extends Thread{
private int threadid;
private int block;
private URL url;
private File file;
@Override
public void run() {
int start = threadid*block;//计算该线程在网络文件的起始下载位置
int end = (threadid+1)*block-1;//计算该线程从网络文件的结束下载位置
try {
RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
accessFile.seek(start);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");
conn.setRequestProperty("Range","bytes="+start+"-"+end);//发送下载网络文件某一个区域的请求
if(conn.getResponseCode()==206){ //区域下载请求成功的状态码为206
InputStream inStream = conn.getInputStream();
byte[] buf = new byte[1024];
int len = 0;
while((len = inStream.read(buf))!=-1){ //这里错误调用了read(),导致连接超时或连接重置
accessFile.write(buf,0,len);
}
accessFile.close();
inStream.close();
}
System.out.println("第"+(threadid+1)+"线程已经下载完成");
} catch (Exception e) {
e.printStackTrace();
}
}
public DownloadThread(int threadid, int block, URL url, File file) {
this.block = block;
this.file = file;
this.threadid = threadid;
this.url = url;
}
}