筆記如下
-
什么是多線程下載?
圖二 多線程下載的分析.jpg - 首先要獲得要下載文件的總大小并創(chuàng)建規(guī)定大小的空文件
//拿到文件的大小
int length = conn.getContentLength();
//getPathName(path)是得到文件名稱
File file = new File(getPathName(path));
//首先創(chuàng)建規(guī)定大小的空文件
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.setLength(length);
raf.close();
- 每條線程的下載的起始位置于結(jié)束位置公式
//去啟動線程去下載文件
//threadId :線程的id號
//threadCount :開幾條線程----3條
for (int threadId = 0; threadId < threadCount; threadId++) {
int startIndex = threadId*blockSize;
int endIndex = (threadId+1)*blockSize-1;
if (threadId==(threadCount-1)) {
endIndex = length-1;
}
//System.out.println("第"+threadId+"線程:下載 從"+startIndex +"~"+endIndex);
//開啟線程下載
new DownloadFilePartThread(threadId, startIndex, endIndex).start();
}
- 通過請求頭告訴服務器要下載的內(nèi)容
conn.setRequestProperty("range", "bytes=" +startIndex+"-"+endIndex);
- 將服務器傳回的數(shù)據(jù)寫到文件中
//拿到數(shù)據(jù)
InputStream in = conn.getInputStream();
//告訴從哪個位子開始寫
//raf.seek(startIndex);
int len=0;
byte[] buf = new byte[1024];
while ((len=in.read(buf))>0) {
raf.write(buf,0,len);
//將實時的位置記錄下來,方便下面緊接著去往文件中去寫
currentPostion=currentPostion+len;
//實現(xiàn)斷點下載
File info = new File(threadId+".position");
OutputStream out = new FileOutputStream(info);
//以字符串的值記錄當前緩存的配置
//out.write(String.valueOf(currentPostion).getBytes());
out.write((currentPostion+"").getBytes());
out.close();
}
- 實現(xiàn)斷點下載
斷點下載就是記錄上次下載停止的地方,設置為開始地方繼續(xù)下載------將停止地點記錄到文件中.
//將實時的位置記錄下來,方便下面緊接著去往文件中去寫
currentPostion=currentPostion+len;
//實現(xiàn)斷點下載
File info = new File(threadId+".position");
OutputStream out = new FileOutputStream(info);
//以字符串的值記錄當前緩存的配置
//out.write(String.valueOf(currentPostion).getBytes());
out.write((currentPostion+"").getBytes());
out.close();
//讀取之前已經(jīng)下載的地方,繼續(xù)下載
File ilf = new File(threadId+".position");
if (ilf.exists() && ilf.length()>0) {
BufferedReader br = new BufferedReader(new FileReader(ilf));
String vl = br.readLine();
int alreadyWritePosition = Integer.valueOf(vl);
//重新設置http請求頭,設置起始下載位置
conn.setRequestProperty("range", "bytes=" +alreadyWritePosition+"-"+endIndex);
//告訴從哪個位子開始寫
raf.seek(alreadyWritePosition);
System.out.println("表示之前下載過");
}else{
conn.setRequestProperty("range", "bytes=" +startIndex+"-"+endIndex);
//告訴從哪個位子開始寫
raf.seek(startIndex);
System.out.println("表示之前沒有下載過");
}
- 文件下載完成后,還要刪除記錄位置的文件
synchronized (NultiThreadDownload.class) {
currentRunningThread--;
if (currentRunningThread <= 0) {
// 將記錄下載位置的文件給刪掉
for (threadId = 0; threadId < threadCount; threadId++) {
File fff = new File(threadId + ".position");
fff.renameTo(new File(threadId + ".position.finish"));
File fll = new File(threadId + ".position.finish");
fll.delete();
}
}
}
- 全部源碼
public class MultiThreadDownload {
//規(guī)定用三條線程去下載
private static int threadCount = 3;
//線程計數(shù)器
private static int currentRunningThread = 3;
//private static String path="http://169.254.210.207:8080/file.txt";
private static String path="http://169.254.210.207:8080/ff.exe";
public static void main(String[] args){
//1.向服務器發(fā)請求,拿到要下載的文件的長度是多少
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
if (code == 200) {
//拿到文件的大小
int length = conn.getContentLength();
//getPathName(path)是得到文件名稱
//得到文件路徑
File file = new File(getPathName(path));
//首先創(chuàng)建規(guī)定大小的空文件
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.setLength(length);
raf.close();
//每塊線程下載的平均大小
int blockSize = length/threadCount;
//去啟動線程去下載文件
//threadId :線程的id號
//threadCount :開幾條線程----3條
for (int threadId = 0; threadId < threadCount; threadId++) {
int startIndex = threadId*blockSize;
int endIndex = (threadId+1)*blockSize-1;
if (threadId==(threadCount-1)) {
endIndex = length-1;
}
//System.out.println("第"+threadId+"線程:下載 從"+startIndex +"~"+endIndex);
new DownloadFilePartThread(threadId, startIndex, endIndex).start();
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static class DownloadFilePartThread extends Thread{
//線程的id號
private int threadId;
//線程的開始位置
private int startIndex;
//線程的結(jié)束位置
private int endIndex;
//當前線程下載位置
private int currentPostion;
public DownloadFilePartThread(int threadId,int startIndex,int endIndex){
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
currentPostion = startIndex;
}
@Override
public void run() {
// TODO Auto-generated method stub
//干下載耗時的事
System.out.println("第"+threadId+"線程:開始下載了..... 從"+startIndex +"~"+endIndex);
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
//在多線程下載中只需要目標文件的一部分的數(shù)據(jù)
//需要告訴服務器,要下載的內(nèi)容
//需要設置http請求頭可以實現(xiàn)
//startIndex ~ endIndex
conn.setRequestProperty("range", "bytes=" +startIndex+"-"+endIndex);
File file = new File(getPathName(path));
RandomAccessFile raf = new RandomAccessFile(file, "rw");
//讀取之前已經(jīng)下載的地方,繼續(xù)下載
File ilf = new File(threadId+".position");
if (ilf.exists() && ilf.length()>0) {
BufferedReader br = new BufferedReader(new FileReader(ilf));
String vl = br.readLine();
int alreadyWritePosition = Integer.valueOf(vl);
//重新設置http請求頭,設置起始下載位置
conn.setRequestProperty("range", "bytes=" +alreadyWritePosition+"-"+endIndex);
//告訴從哪個位子開始寫
raf.seek(alreadyWritePosition);
System.out.println("表示之前下載過");
}else{
conn.setRequestProperty("range", "bytes=" +startIndex+"-"+endIndex);
//告訴從哪個位子開始寫
raf.seek(startIndex);
System.out.println("表示之前沒有下載過");
}
//多線程下載返回----206
int code = conn.getResponseCode();
if (code == 206) {
//拿到數(shù)據(jù)
InputStream in = conn.getInputStream();
//告訴從哪個位子開始寫
//raf.seek(startIndex);
int len=0;
byte[] buf = new byte[1024];
while ((len=in.read(buf))>0) {
raf.write(buf,0,len);
//將實時的位置記錄下來,方便下面緊接著去往文件中去寫
currentPostion=currentPostion+len;
//實現(xiàn)斷點下載
File info = new File(threadId+".position");
OutputStream out = new FileOutputStream(info);
//以字符串的值記錄當前緩存的配置
//out.write(String.valueOf(currentPostion).getBytes());
out.write((currentPostion+"").getBytes());
out.close();
}
in.close();
raf.close();
}
System.out.println("第"+threadId+"線程:下載結(jié)束了..... ");
//等到線程完成后再去刪文件
//弄一個計數(shù)器,記住總共有多少線程下載,每當一個線程下載后就-1
//當計數(shù)器小于0或等于0的時候,就說明沒有線程在下載了,就刪除記錄下位置的文件
synchronized (MultiThreadDownload.class) {
currentRunningThread--;
if (currentRunningThread <= 0) {
// 將記錄下載位置的文件給刪掉
for (threadId = 0; threadId < threadCount; threadId++) {
File fff = new File(threadId + ".position");
fff.renameTo(new File(threadId + ".position.finish"));
File fll = new File(threadId + ".position.finish");
fll.delete();
}
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static String getPathName(String path){
int index = path.lastIndexOf("/");
return path.substring(index+1);
}
}