背景
万事都有两面性,多线程下载也是,那么多线程下载的优点是什么呢?归根结底还是多线程的优点,这里我们暂且不去讨论它的利弊,只是讲解一下思想和实现方案。
实现分析
我们用五个why的思想来分析一下这个问题:
- 怎么实现多线程下载?
将下载逻辑在多个线程中同时运行。 - 怎么让每个线程下载对应的文件?
将文件拆分成线程数对应的分数,进行分配。 - 怎么拆分文件?
获取文件的长度,再按照线程数进行按比例分配。 - 怎么获取文件长度?
利用HttpURLConnection的方法来获取内容长度 - 下载完成之后怎么办?
各个线程都下载完成之后利用RandomAccessFile进行文件合并
好了,分析到这我们感觉已经可以实现了,我们再重新梳理一下逻辑,大概是,设定线程的数量,按照线程数量来分割要下载的文件,启动多个线程进行下载,最后合成一个文件。OK,撸起袖子就是干!
代码实现
1、设置线程数,我这边是默认指定了三个,大家也可以通过服务器配置啊,或者某些算法来计算需要的线程数,根据实际情况来定。
2、获取文件长度:
URL url = new URL(file.url);
HttpURLConnection con = (HttpURLConnection)
url.openConnection();
con.setRequestMethod("GET");
con.setConnectTimeout(5000);
if(con.getResponseCode() == HttpURLConnection.HTTP_OK) {
int len = con.getContentLength(); //文件的总长度
}
这样我们就获取到了文件的长度,然后就可以分割下载了,当然之前我们要初始化一些路径啊,RandomAccessFile什么的,大家可以下载源码查看。
3、分割文件内容:
List<ThreadInfo> threadInfoList = new LinkedList<ThreadInfo>(); //建立线程信息列表
int block = mDownloadInfo.lenght/mThreadCount; //将下载文件分段
if(block > 0) {
//start 根据线程数量分别建立线程信息
for(int i = 0;i < mThreadCount;i++) {
ThreadInfo info = new ThreadInfo(i,mDownloadInfo.url,i*block,(i+1)*block-1,0);
if(i == mThreadCount -1) {
info.end = mDownloadInfo.lenght; //分段最后一个,结束位置到文件总长度末尾
}
threadInfoList.add(info); //加入列表
}
//end 根据线程数量分别建立线程信息
4、启动下载线程:
//start 启动下载线程
for(ThreadInfo info : threadInfoList) {
DownloadThread thread = new DownloadThread(info,mDownloadInfo,mTotalFinished);
if(!mThreadPool.isShutdown()) {
mThreadPool.execute(thread);
}
}
5、下载的逻辑和RandomAccessFile最后生成一个完整的文件:
public void run() {
URL url = null;
HttpURLConnection con = null; //http链接
RandomAccessFile accessFile = null; //下载文件
InputStream inputStream = null; //输入流
try {
int start = threadInfo.start+threadInfo.finished; //读取文件的位置
//start 初始化下载链接
url = new URL(threadInfo.url);
con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setConnectTimeout(5000);
con.setRequestProperty("Range", "bytes=" + start + "-" + threadInfo.end); //设置读取文件的位置,和结束位置
//end 初始化下载链接
//start 初始化下载到本地的文件
accessFile = new RandomAccessFile(new File(downloadInfo.filePath, downloadInfo.fileName),"rwd");
accessFile.seek(start); //设置开始写入的位置
//end 初始化下载到本地的文件
int responseCode = con.getResponseCode();
if((con.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) ||
(con.getResponseCode() == HttpURLConnection.HTTP_OK) ) {
inputStream = con.getInputStream();
int finished = threadInfo.finished; //已经下载的长度
int readLen = -1; //读取的长度
byte[] buffer = new byte[1024*4];
long time = System.currentTimeMillis();
//start 读取输入流写入文件
while((readLen = inputStream.read(buffer))!=-1) {
accessFile.write(buffer, 0, readLen);
);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(inputStream!=null){
inputStream.close();
}
if(accessFile!=null) {
accessFile.close();
}
if(null!=con) {
con.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
super.run();
}
总结
好了,主要下载逻辑就是这样,大家想看完整代码的可以点击下面的链接,希望大家可以喜欢,谢谢!
源码下载