背景
萬事都有兩面性,多線程下載也是隘冲,那么多線程下載的優(yōu)點(diǎn)是什么呢?歸根結(jié)底還是多線程的優(yōu)點(diǎn)绑雄,這里我們暫且不去討論它的利弊展辞,只是講解一下思想和實(shí)現(xiàn)方案。
實(shí)現(xiàn)分析
我們用五個(gè)why的思想來分析一下這個(gè)問題:
- 怎么實(shí)現(xiàn)多線程下載万牺?
將下載邏輯在多個(gè)線程中同時(shí)運(yùn)行罗珍。 - 怎么讓每個(gè)線程下載對(duì)應(yīng)的文件?
將文件拆分成線程數(shù)對(duì)應(yīng)的分?jǐn)?shù)脚粟,進(jìn)行分配覆旱。 - 怎么拆分文件?
獲取文件的長(zhǎng)度核无,再按照線程數(shù)進(jìn)行按比例分配扣唱。 - 怎么獲取文件長(zhǎng)度?
利用HttpURLConnection的方法來獲取內(nèi)容長(zhǎng)度 - 下載完成之后怎么辦团南?
各個(gè)線程都下載完成之后利用RandomAccessFile進(jìn)行文件合并
好了噪沙,分析到這我們感覺已經(jīng)可以實(shí)現(xiàn)了,我們?cè)僦匦率崂硪幌逻壿嬐赂蟾攀钦撸O(shè)定線程的數(shù)量,按照線程數(shù)量來分割要下載的文件拷橘,啟動(dòng)多個(gè)線程進(jìn)行下載局义,最后合成一個(gè)文件。OK冗疮,擼起袖子就是干旭咽!
代碼實(shí)現(xiàn)
1、設(shè)置線程數(shù)赌厅,我這邊是默認(rèn)指定了三個(gè),大家也可以通過服務(wù)器配置啊轿塔,或者某些算法來計(jì)算需要的線程數(shù)特愿,根據(jù)實(shí)際情況來定。
2勾缭、獲取文件長(zhǎng)度:
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(); //文件的總長(zhǎng)度
}
這樣我們就獲取到了文件的長(zhǎng)度揍障,然后就可以分割下載了,當(dāng)然之前我們要初始化一些路徑啊俩由,RandomAccessFile什么的毒嫡,大家可以下載源碼查看。
3幻梯、分割文件內(nèi)容:
List<ThreadInfo> threadInfoList = new LinkedList<ThreadInfo>(); //建立線程信息列表
int block = mDownloadInfo.lenght/mThreadCount; //將下載文件分段
if(block > 0) {
//start 根據(jù)線程數(shù)量分別建立線程信息
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; //分段最后一個(gè)兜畸,結(jié)束位置到文件總長(zhǎng)度末尾
}
threadInfoList.add(info); //加入列表
}
//end 根據(jù)線程數(shù)量分別建立線程信息
4努释、啟動(dòng)下載線程:
//start 啟動(dòng)下載線程
for(ThreadInfo info : threadInfoList) {
DownloadThread thread = new DownloadThread(info,mDownloadInfo,mTotalFinished);
if(!mThreadPool.isShutdown()) {
mThreadPool.execute(thread);
}
}
5、下載的邏輯和RandomAccessFile最后生成一個(gè)完整的文件:
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); //設(shè)置讀取文件的位置咬摇,和結(jié)束位置
//end 初始化下載鏈接
//start 初始化下載到本地的文件
accessFile = new RandomAccessFile(new File(downloadInfo.filePath, downloadInfo.fileName),"rwd");
accessFile.seek(start); //設(shè)置開始寫入的位置
//end 初始化下載到本地的文件
int responseCode = con.getResponseCode();
if((con.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) ||
(con.getResponseCode() == HttpURLConnection.HTTP_OK) ) {
inputStream = con.getInputStream();
int finished = threadInfo.finished; //已經(jīng)下載的長(zhǎng)度
int readLen = -1; //讀取的長(zhǎng)度
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();
}
總結(jié)
好了伐蒂,主要下載邏輯就是這樣,大家想看完整代碼的可以點(diǎn)擊下面的鏈接肛鹏,希望大家可以喜歡逸邦,謝謝!
源碼下載