標籤:多線程下載 快播 迅雷
多線程下載可以搶佔其它相同優先順序使用者的網路資源(寬頻),所以說下載速度比較快,迅雷、快播都使用了多線程下載。
1.請求伺服器上的檔案的長度2.根據伺服器上的檔案長度在手機上建立一個一模一樣大小的檔案3.根據線程的個數和檔案的長度來計算每一個線程需要下載的範圍 檔案的長度為:10 線程的數量為:3 每一塊的大小:10/3=3.3333=3
線程0:0~2
線程1:3~5
線程2:6~9
開始的位置:線程的id*每一塊的大小。 結束的位置:線程的(id+1 ) *每一塊的大小 -1; 最後一個線程結束的位置是:檔案的長度-1;
4.開啟多個線程,每一個線程下載自己的那區塊範圍 1).請求伺服器上傳的部分內容。
Range(要求標頭): bytes=3-8擷取伺服器檔案3-8的內容
特點:伺服器返回值不再是200,是206.
2).把寫入的位置移動到線程開始的位置上。
3).迴圈的讀取內容寫入到本地。
####斷點下載(非正常關閉)*在每一次讀取到資料,寫入本機資料時,應該把總記錄存到本地。*當下一次開始下載時,先查看有沒有下載的記錄(存檔),如果有就取出來,繼續下載
代碼:
public class DownloadUtils {// 完成的線程private int completeThread;private int threadCount;/** * 開始多線程下載 * * @param url * 訪問的伺服器位址 * @param dir * 本地接收路徑 * @param threadCount * 開啟多少條線程下載 */public void startMultiThreadDownload(final String url, final String dir,final int threadCount) {this.threadCount = threadCount;completeThread = 0;new Thread(new Runnable() {@Overridepublic void run() {// 1.請求伺服器上的檔案的長度int fileLength = getRemoteServiceFileLength(url);if (fileLength == -1) {System.out.println("請求伺服器檔案長度失敗,停止下載");return;}System.out.println("遠程伺服器的長度為:" + fileLength);// 2.根據伺服器上的檔案長度在手機上建立一個一模一樣大小的檔案String fileName = url.substring(url.lastIndexOf("/") + 1);File file = new File(dir, fileName);try {RandomAccessFile raf = new RandomAccessFile(file, "rwd");raf.setLength(fileLength);raf.close();System.out.println("本地檔案建立成功" + file.getPath());} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}// 3.根據線程的個數和檔案的長度來計算每一個線程需要下載的範圍int blockSize = fileLength / threadCount;for (int i = 0; i < threadCount; i++) {// 開始的位置:線程的id*每一塊的大小。int start = i * blockSize;// 結束的位置:線程的(id+1 ) *每一塊的大小 -1;int end = (i + 1) * blockSize - 1;// 最後一個線程結束的位置是:檔案的長度-1;if ((i == threadCount - 1)) {end = fileLength - 1;}// 4.開啟多個線程,每一個線程下載自己的那區塊範圍,傳遞參數:,隱藏檔的路徑// 開始的位置,結束的位置,線程IDnew Thread(new DownloadRunnable(url, file.getPath(), start,end, i)).start();}}}).start();}/** * 下載任務類 * * @author Administrator * */class DownloadRunnable implements Runnable {private String url;private File localFile; // 檔案的路徑private int start; // 當前線程開始下載的位置private int end; // 結束下載的位置private int threadID;// 線程IDpublic DownloadRunnable(String url, String path, int start, int end,int i) {this.url = url;this.localFile = new File(path);this.start = start;this.end = end;this.threadID = i;}@Overridepublic void run() {HttpURLConnection conn = null;try {// 添加斷點下載int total = 0;// 當前線程下載的總進度// 把總進度值儲存到本地去File cacheFile = new File(localFile.getParent(), "thread"+ threadID + ".hm");RandomAccessFile cacheRAF = new RandomAccessFile(cacheFile,"rwd");// 判斷上一次是否有緩衝的進度if (cacheFile.exists() && cacheFile.length() > 0) {// 檔案存在並且有資料// 上一次下載的進度int progress = Integer.valueOf(cacheRAF.readLine());start += progress;// 把上一次的進度在開始的位置加上,繼續下載total = progress;// 把上一次下載總進度賦值}System.out.println("線程" + threadID + ",開始下載了,下載的範圍是:" + start+ " ~" + end);conn = (HttpURLConnection) new URL(url).openConnection();conn.setRequestMethod("GET");conn.setConnectTimeout(5000);conn.setReadTimeout(5000);// 佈建要求頭訊息:Range,請求伺服器上檔案的部分內容conn.setRequestProperty("Range", "bytes=" + start + "-" + end);int responseCode = conn.getResponseCode();if (responseCode == 206) {int contentLength = conn.getContentLength();System.out.println("線程" + threadID + ",請求成功,內容長度為:"+ contentLength);// 得到伺服器返回的資料InputStream is = conn.getInputStream();RandomAccessFile raf = new RandomAccessFile(localFile,"rwd");// 把寫入的位置移動到線程開始的位置上raf.seek(start);byte[] buf = new byte[1024];int len = 0;while ((len = is.read(buf)) != -1) {raf.write(buf, 0, len);total += len;cacheRAF.seek(0);// 每一次都移動到檔案的開始位置進行寫入cacheRAF.write(String.valueOf(total).getBytes());}cacheRAF.close();raf.close();is.close();System.out.println("線程" + threadID + ",下載完成了.");completeThread++;if (completeThread == threadCount) {System.out.println("全部下載完整,刪除臨時設定檔.");for (int i = 0; i < threadCount; i++) {File deleteFile = new File(localFile.getParent(),"thread" + i + ".hm");System.out.println(deleteFile.delete());}}}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {if (conn != null) {conn.disconnect();}}}}/** * 根據URL串連請求遠程伺服器擷取檔案大小 * * @param url * 伺服器位址 * @return */private int getRemoteServiceFileLength(String url) {HttpURLConnection conn = null;try {conn = (HttpURLConnection) new URL(url).openConnection();// 請求方式,必須大小conn.setRequestMethod("GET");// 連線逾時conn.setConnectTimeout(5000);// 讀取逾時conn.setReadTimeout(5000);int reponseCode = conn.getResponseCode();// 擷取的伺服器返回的響應碼if (reponseCode == 200) {return conn.getContentLength();}} catch (Exception e) {e.printStackTrace();} finally {if (conn != null) {conn.disconnect();}}return -1;}}
調用:
public class Demo {public static void main(String[] args) {System.out.println("開啟多線程下載了............");// 連結,路徑,線程的數量String url = "http://192.168.1.109/FeiQ.exe";String dir = "F:/WDJDownload";int threadCount = 3;DownloadUtils du = new DownloadUtils();du.startMultiThreadDownload(url, dir, threadCount);}}