我們把服務(wù)器的上的文件看作水缸的水荡陷,要想取走水缸里面的水的話屈呕,那么多線程下載就相當(dāng)于用多條管子抽水一樣狡恬。
多線程下載大約可以分為以下幾個(gè)步驟:
1.要知道服務(wù)端資源的大小珠叔。
2、在本地創(chuàng)建一個(gè)與服務(wù)器資源同樣大小的一個(gè)文件弟劲,主要是用來占位祷安;
Paste_Image.png
//file : 文件; mode:文件的模式兔乞,rwd:直接寫到底層設(shè)備汇鞭,硬盤
RandomAccessFile randomfile =new RandomAccessFile(File file,String mode)
3、要分配每個(gè)線程下載文件的開始位置和結(jié)束位置庸追。那該如何確定每個(gè)線程下載文件的開始位置和結(jié)束位置呢霍骄?
Paste_Image.png
a、如圖所示我們假設(shè)開啟3個(gè)線程淡溯,去下載資源大小為10的文件
b.首先計(jì)算出每個(gè)線程下載的長度 blocKSize=資源長度/線程數(shù)量
c读整、每個(gè)線程下載文件的起始位置。最后一個(gè)線程的末位需要單獨(dú)計(jì)算
d咱娶、那么就是i線程的下載起始位置: (i+1)*blocKSize-1
e米间、最后一個(gè)線程的末位為,資源長度-1
4膘侮、開啟多個(gè)線程车伞,每一個(gè)線程下載對應(yīng)位置的文件即可。
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class DowloadFile{
private static int threadCount = 3;//開啟3個(gè)線程
private static int blockSize = 0;//每個(gè)線程下載的大小
private static int runningTrheadCount = 0;//當(dāng)前運(yùn)行的線程數(shù)
private static String path = "http://192.168.13.83:8080/QQ.exe";
public static void main(String[] args) {
try{
//1.請求url地址獲取服務(wù)端資源的大小
URL url = new URL(path);
HttpURLConnection openConnection = (HttpURLConnection) url.openConnection();
openConnection.setRequestMethod("GET");
openConnection.setConnectTimeout(10*1000);
int code = openConnection.getResponseCode();
if(code == 200){
//獲取資源的大小
int filelength = openConnection.getContentLength();
//2.在本地創(chuàng)建一個(gè)與服務(wù)端資源同樣大小的一個(gè)文件(占位)
RandomAccessFile randomAccessFile = new RandomAccessFile(new File(getFileName(path)), "rw");
randomAccessFile.setLength(filelength);//設(shè)置隨機(jī)訪問文件的大小
//3.要分配每個(gè)線程下載文件的開始位置和結(jié)束位置喻喳。
blockSize = filelength/threadCount;//計(jì)算出每個(gè)線程理論下載大小
for(int threadId =0 ;threadId < threadCount;threadId++){
int startIndex = threadId * blockSize;//計(jì)算每個(gè)線程下載的開始位置
int endIndex = (threadId+1)*blockSize -1;//計(jì)算每個(gè)線程下載的結(jié)束位置
//如果是最后一個(gè)線程,結(jié)束位置需要單獨(dú)計(jì)算
if(threadId == threadCount-1){
endIndex = filelength -1;
}
//4.開啟線程去執(zhí)行下載
new DownloadThread(threadId, startIndex, endIndex).start();
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
/**
* 下載文件的子線程困曙,每個(gè)線程下載對應(yīng)的文件
*
*/
public static class DownLoadThread extends Thread{
private int threadId;
private int startIndex;
private int endIndex;
/**
* @param threadId線程ID
* @param startIndex
* @param endIndex
*/
public DownLoadThread(int threadId, int startIndex, int endIndex) {
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
@Override
public void run() {
try{
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
//很重要:請求服務(wù)器下載部分的文件的指定的位置:
conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
conn.setConnectTimeout(5000);
int code = conn.getResponseCode();//從服務(wù)器請求全部資源 200ok ,如果請求部分資源 206 ok
System.out.println("code="+code);
InputStream is = conn.getInputStream();//返回資源
RandomAccessFile raf = new RandomAccessFile("QQ.exe", "rwd");
//隨機(jī)寫文件的時(shí)候從哪個(gè)位置開始寫
raf.seek(startIndex);//定位文件
int len =0;
byte[] buffer = new byte[1024];
while((len = is.read(buffer)) != -1){
raf.write(buffer,0,len);
}
is.close();
raf.close();
System.out.println("線程"+threadId+"下載完畢");
}catch(Exception e){
e.printStackTrace();
}
}
}
}