在上一章中我們實現了多線程下載功能,這裡我們添加斷點下載功能,防止下載過程中程式意外退出。具體代碼如下:
package com.jwzhangjie;/** * 說明: * 每一個線程下載的位置計算方式: * 開始位置: * (線程id - 1)*每一塊大小 * 結束位置: * (線程id*每一塊大小) - 1 * ---注意有時候不一定能夠整除,所以最後一個線程的結束位置應該是檔案的末尾 * * 步驟: * 1.本地建立一個大小跟伺服器檔案相同的臨時檔案 * 2.計算分配幾個線程去下載伺服器上的資源,知道每個線程下載檔案的位置 * 3.開啟三個線程,每一個線程下載對應位置的檔案 * 4.如果所有的線程,都把自己的資料下載完畢後,伺服器上的資源都被下載到本地了 * * 斷點下載: * 1.使用檔案記錄每一個線程的下載長度 * 2.每一個下載開始之前,讀取檔案,如果檔案存在並且長度大於0,則取出長度 * 3.將每一個線程的起始位置+已經下載的長度 * 4.所有的線程下載完畢後,刪除儲存下載長度的檔案 */import java.io.File;import java.io.FileInputStream;import java.io.InputStream;import java.io.RandomAccessFile;import java.net.HttpURLConnection;import java.net.URL;public class Demo {public static String path = "http://192.168.5.103:8080/examples/downloader.exe";//"http://softdownload.hao123.com/hao123-soft-online-bcs/soft/Y/2013-07-18_YoudaoDict_baidu.alading.exe";public static int threadCount = 3;public static int runningThread = 3;public static void main(String[] args) throws Exception{//1.串連伺服器,擷取一個檔案,擷取檔案的長度,在本地建立一個跟伺服器一樣大小的臨時檔案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();System.out.println("檔案總長度:"+length);//在用戶端本地建立出來一個大小跟伺服器端一樣大小的臨時檔案RandomAccessFile raf = new RandomAccessFile("setup.exe", "rwd");//指定建立的這個檔案的長度raf.setLength(length);raf.close();//假設是3個線程去下載資源。//平均每一個線程下載的檔案大小.int blockSize = length / threadCount;for (int threadId = 1; threadId <= threadCount; threadId++) {//第一個線程下載的開始位置int startIndex = (threadId - 1) * blockSize;int endIndex = threadId * blockSize - 1;if (threadId == threadCount) {//最後一個線程下載的長度要稍微長一點endIndex = length;}System.out.println("線程:"+threadId+"下載:---"+startIndex+"--->"+endIndex);new DownLoadThread(path, threadId, startIndex, endIndex).start();}}else {System.out.printf("伺服器錯誤!");}}/** * 下載檔案的子線程 每一個線程下載對應位置的檔案 * @author jie * */public static class DownLoadThread extends Thread{private int threadId;private int startIndex;private int endIndex;/** * @param path 下載檔案在伺服器上的路徑 * @param threadId 線程Id * @param startIndex 線程下載的開始位置 * @param endIndex線程下載的結束位置 */public DownLoadThread(String path, int threadId, int startIndex, int endIndex) {super();this.threadId = threadId;this.startIndex = startIndex;this.endIndex = endIndex;}@Overridepublic void run() {try {//檢查是否存在記錄下載長度的檔案,如果存在讀取這個檔案File tmp_file = new File(threadId+".txt");if (tmp_file.exists() && tmp_file.length() > 0) {FileInputStream fio = new FileInputStream(tmp_file);byte[] temp = new byte[1024];int len = fio.read(temp);String downloadlen = new String(temp, 0, len);int downloadInt = Integer.parseInt(downloadlen);startIndex = downloadInt;//修改下載的真實的開始位置System.out.println("線程:"+threadId+"真實的下載位置:"+startIndex+"--->"+endIndex);fio.close();}URL url = new URL(path);HttpURLConnection conn = (HttpURLConnection)url.openConnection();conn.setConnectTimeout(5000);conn.setRequestMethod("GET");//重要:請求伺服器下載部分檔案 指定檔案的位置conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);//從伺服器請求全部資源返回200 ok如果從伺服器請求部分資源 返回 206 okint code = conn.getResponseCode();System.out.println("code:"+code);InputStream is = conn.getInputStream();//已經設定了請求的位置,返回的是當前位置對應的檔案的輸入資料流RandomAccessFile raf = new RandomAccessFile("setup.exe", "rwd");//隨機寫檔案的時候從哪個位置開始寫raf.seek(startIndex);//定位檔案int len = 0;byte[] buffer = new byte[1024];int total = 0;//已經下載的資料長度while ((len = is.read(buffer)) != -1) {RandomAccessFile file = new RandomAccessFile(threadId+".txt", "rwd");raf.write(buffer, 0, len);total += len;file.write((""+(total+startIndex)).getBytes());file.close();}is.close();raf.close();System.out.println("線程:"+threadId+"下載完畢");} catch (Exception e) {e.printStackTrace();}finally{runningThread--;if (runningThread == 0) {//所有的線程執行完畢for (int i = 1; i <= threadCount; i++) {File file = new File(i+".txt");file.delete();}System.out.println("檔案全部下載完畢!");}}}}}
實驗測試結果:
第一次下載:
檔案總長度:5562040線程:1下載:---0--->1854012線程:2下載:---1854013--->3708025線程:3下載:---3708026--->5562040code:206code:206code:206
第二次下載:
檔案總長度:5562040線程:1下載:---0--->1854012線程:2下載:---1854013--->3708025線程:3下載:---3708026--->5562040線程:1真實的下載位置:21504--->1854012線程:2真實的下載位置:1871421--->3708025線程:3真實的下載位置:3726458--->5562040code:206code:206code:206
下載完畢:
檔案總長度:5562040線程:1下載:---0--->1854012線程:2下載:---1854013--->3708025線程:3下載:---3708026--->5562040線程:2真實的下載位置:3512893--->3708025線程:3真實的下載位置:5268602--->5562040線程:1真實的下載位置:1677312--->1854012code:206code:206code:206線程:1下載完畢線程:2下載完畢線程:3下載完畢檔案全部下載完畢!
如下:
下載的檔案能夠使用,說明下載成功。下載工具UI版明天更新。