原理
將要已經下載進度保存到臨時文件中,例如:下載了3000字節的話,也就是保存了0~2999的位置,下次從3000的位置開始繼續下載,那麼把3000保存到臨時文件中即可。
對於多線程的話,我們也要分別進行記錄,有幾個線程則保存幾個文件,
例如
0號線程:下載了6000字節 記錄 0~5999 下次從6000字節開始
1號線程:下載了4000字節 記錄 0~3999 下次從4000字節開始
2號線程:下載了500字節 記錄 0~499 下次從500字節開始
計算:
例如:
下載區間爲100-300
已經下載120 即 100-219
下次下載應該從220開始下載。
即:newStartIdx =startInx + total
帶斷點續傳的多線程下載
定義一個int變量記錄每條線程下載的數據總長度,然後加上該線程的下載開始位置,得到的結果就是下次下載時,該線程的開始位置,把得到的結果存入緩存文件
//用來記錄當前線程總的下載長度 int total = 0; while((len = is.read(b)) != -1){ raf.write(b, 0, len); total += len; //每次下載都把新的下載位置寫入緩存文本文件 RandomAccessFile raf2 = new RandomAccessFile(threadId + ".txt", "rwd"); raf2.write((startIndex + total + "").getBytes()); raf2.close(); }
下次下載開始時,先讀取緩存文件中的值,得到的值就是該線程新的開始位置
FileInputStream fis = new FileInputStream(file); BufferedReader br = new BufferedReader(new InputStreamReader(fis)); String text = br.readLine(); int newStartIndex = Integer.parseInt(text); //把讀到的值作爲新的開始位置 startIndex = newStartIndex; fis.close();
三條線程都下載完畢之後,刪除緩存文件
RUNNING_THREAD--; if(RUNNING_THREAD == 0){ for(int i = 0; i <= 3; i++){ File f = new File(i + ".txt"); f.delete(); } }
核心代碼
public class MultiDownload { static int ThreadCount = 3; static int finishedThread = 0; //確定下載地址 static String path = "http://192.168.1.13:8080/QQ.exe"; public static void main(String[] args) { //發送get請求,請求這個地址的資源 try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); if(conn.getResponseCode() == 200){ //拿到所請求資源文件的長度 int length = conn.getContentLength(); File file = new File("local_qq.exe"); //生成臨時文件--佔用磁盤空間--可以防止磁盤空間不夠用 RandomAccessFile raf = new RandomAccessFile(file, "rwd"); //設置臨時文件的大小 raf.setLength(length); raf.close(); //計算出每個線程應該下載多少字節 int size = length / ThreadCount; for (int i = 0; i < ThreadCount; i++) { //計算線程下載的開始位置和結束位置 int startIndex = i * size; int endIndex = (i + 1) * size - 1; //如果是最後一個線程,那麼結束位置寫死 if(i == ThreadCount - 1){ endIndex = length - 1; } // System.out.println("線程" + i + "的下載區間是:" + startIndex + "---" + endIndex); new DownLoadThread(startIndex, endIndex, i).start(); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class DownLoadThread extends Thread{ int startIndex; //下載的開始位置 int endIndex; //下載的結束位置 int threadId; //下載的線程Id public DownLoadThread(int startIndex, int endIndex, int threadId) { super(); this.startIndex = startIndex; this.endIndex = endIndex; this.threadId = threadId; } @Override public void run() { //再次發送http請求,下載原文件 try { File progressFile = new File(threadId + ".txt"); //判斷進度臨時文件是否存在 if(progressFile.exists()){ FileInputStream fis = new FileInputStream(progressFile); BufferedReader br = new BufferedReader(new InputStreamReader(fis)); //從進度臨時文件中讀取出上一次下載的總進度,然後與原本的開始位置相加,得到新的開始位置 startIndex += Integer.parseInt(br.readLine()); fis.close(); } System.out.println("線程" + threadId + "的下載區間是:" + startIndex + "---" + endIndex); HttpURLConnection conn; URL url = new URL(MultiDownload.path); conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); //設置本次http請求所請求的數據的區間 conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); //請求部分數據,相應碼是206 if(conn.getResponseCode() == 206){ //流裏此時只有1/3原文件的數據 InputStream is = conn.getInputStream(); byte[] b = new byte[1024]; int len = 0; int total = 0; //拿到臨時文件的輸出流 File file = new File("local_qq.exe"); RandomAccessFile raf = new RandomAccessFile(file, "rwd"); //把文件的寫入位置移動至startIndex raf.seek(startIndex); while((len = is.read(b)) != -1){ //每次讀取流裏數據之後,同步把數據寫入臨時文件 raf.write(b, 0, len); total += len; // System.out.println("線程" + threadId + "下載了" + total); //生成一個專門用來記錄下載進度的臨時文件 RandomAccessFile progressRaf = new RandomAccessFile(progressFile, "rwd"); //每次讀取流裏數據之後,同步把當前線程下載的總進度寫入進度臨時文件中 progressRaf.write((total + "").getBytes()); progressRaf.close(); } System.out.println("線程" + threadId + "下載完畢-------------------"); raf.close(); MultiDownload.finishedThread++; synchronized (MultiDownload.path) { if(MultiDownload.finishedThread == MultiDownload.ThreadCount){ for (int i = 0; i < MultiDownload.ThreadCount; i++) { File f = new File(i + ".txt"); f.delete(); } MultiDownload.finishedThread = 0; } } } } catch (Exception e) { e.printStackTrace(); } } }