Java--使用多線程下載,斷點續傳技術原理(RandomAccessFile)

一.基礎知識

1.什麼是線程?什麼是進程?它們之間的關係?

能夠參考以前的一篇文章:java核心知識點學習----併發和並行的區別,進程和線程的區別,如何建立線程和線程的四種狀態,什麼是線程計時器html

簡單說一個進程能夠由多個線程組成,一個操做系統能夠多個進程,它們都是能夠同時進行工做的.java

2.什麼是下載?如何多線程進行下載?如何斷點續傳?

廣義上說,凡是在屏幕上看到的不屬於本地計算機上的內容,皆是經過"下載"得來。狹義上人們只認爲那些自定義了下載文件的本地磁盤存儲位置的操做纔是"下載";。chrome

WEB下載方式分爲HTTP與FTP兩種類型,它們分別是Hyper Text Transportation Protocol(超文本傳輸協議)與File Transportation Protocol(文件傳輸協議)的縮寫,它們是計算機之間交換數據的方式,也是兩種最經典的下載方式,該下載方式原理很是簡單,就是用戶兩種規則(協議)和提供文件的服務器取得聯繫並將文件搬到本身的計算機中來,從而實現下載的功能。
多線程下載,便是一個文件能過多個線程進行下載;而斷點續傳說的是當一個文件下載到一半時忽然因爲某個緣由下載中斷了,好比忽然電腦關機了,那麼當再開機時已經下載到一半的文件不須要重頭開始,而是接着下載;其原理很簡單:首先,下載中斷時記住上一個時點下載的位置,而後接着這個位置繼續下載,這個繼續下載能夠是人手工觸發的也能夠是程序運行時自動識別進行下載的.apache

3.什麼是RandomAccessFile?

RandomAccessFile的惟一父類是Object,與其餘流父類不一樣。是用來訪問那些保存數據記錄的文件的,這樣你就能夠用seek( )方法來訪問記錄,並進行讀寫了。這些記錄的大小沒必要相同;可是其大小和位置必須是可知的。數組

RandomAccessFile的工做方式是,把DataInputStream和DataOutputStream粘起來,再加上它本身的一些方法,好比定位用的getFilePointer( ),在文件裏移動用的seek( ),以及判斷文件大小的length( )。此外,它的構造函數還要一個表示以只讀方式("r"),仍是以讀寫方式("rw")打開文件的參數 (和C的fopen( )如出一轍)。它不支持只寫文件,從這一點上看,假如RandomAccessFile繼承了DataInputStream,它也許會幹得更好。tomcat

因此,本例中是利用RandomAccessFile的seek記住上次的訪問記錄,而後接着上次的訪問進行下載的.服務器

RandomAccess直譯過來是隨機訪問,這樣理解很容易形成困擾,即然是隨機的,那麼又怎麼來控制進度呢?網絡

由Random這個單詞的示意:多線程

random
[ 'rændəm ]
adj. 隨意的,任意,隨機的,隨機挑選的,任意隨機的,胡亂任意的,隨機性,任意的,胡亂的,隨便的;(話等)信口亂說的;(人等)偶然碰到的,隨意選擇的,隨便,可有可無的,漫不經心的

能夠看到其還有任意的意思,這就代表能夠訪問文件的任意位置,這樣就能解釋爲什麼能夠斷點續傳了.併發

 

二.程序實現

這裏以tomcat的下載爲例:http://tomcat.apache.org/download-70.cgi#7.0.54 (tomcat目前最新的版本是7.0.54,2014-07-02)

F12打開chrome的WebDeveloper NetWork窗口,而後點擊下載,以下圖所示:

 

這裏須要注意的是Request Headers裏的內容,如RequestMethod,Accept,Accept-Language,Connection等,這裏在發出請求時須要將這些東西帶上,這裏找到下載tomcat 7.0.54的下載連接:http://mirrors.cnnic.cn/apache/tomcat/tomcat-7/v7.0.54/bin/apache-tomcat-7.0.54.zip

實現代碼:DownUtil.java

 
 
package com.amos.tool;

import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; /** * Created by amosli on 14-7-2. */

public
class DownUtil { // 定義下載資源的路徑 private String path; // 指定所下載的文件的保存位置 private String targetFile; // 定義須要使用多少線程下載資源 private int threadNum; // 定義下載的線程對象 private DownThread[] threads; // 定義下載的文件的總大小 private int fileSize; public DownUtil(String path, String targetFile, int threadNum) { this.path = path; this.threadNum = threadNum; // 初始化threads數組 threads = new DownThread[threadNum]; this.targetFile = targetFile; } public void download() throws Exception { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5 * 1000); conn.setRequestMethod("GET"); conn.setRequestProperty( "Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, " + "application/x-shockwave-flash, application/xaml+xml, " + "application/vnd.ms-xpsdocument, application/x-ms-xbap, " + "application/x-ms-application, application/vnd.ms-excel, " + "application/vnd.ms-powerpoint, application/msword, */*"); conn.setRequestProperty("Accept-Language", "zh-CN"); conn.setRequestProperty("Charset", "UTF-8"); conn.setRequestProperty("Connection", "Keep-Alive"); // 獲得文件大小 fileSize = conn.getContentLength(); conn.disconnect(); int currentPartSize = fileSize / threadNum + 1;//這裏沒必要必定要加1,不加1也能夠 RandomAccessFile file = new RandomAccessFile(targetFile, "rw"); // 設置本地文件的大小 file.setLength(fileSize); file.close(); for (int i = 0; i < threadNum; i++) { // 計算每條線程的下載的開始位置 int startPos = i * currentPartSize; // 每一個線程使用一個RandomAccessFile進行下載 RandomAccessFile currentPart = new RandomAccessFile(targetFile, "rw"); // 定位該線程的下載位置 currentPart.seek(startPos); // 建立下載線程 threads[i] = new DownThread(startPos, currentPartSize, currentPart); // 啓動下載線程 threads[i].start(); } } // 獲取下載的完成百分比 public double getCompleteRate() { // 統計多條線程已經下載的總大小 int sumSize = 0; for (int i = 0; i < threadNum; i++) { sumSize += threads[i].length; } // 返回已經完成的百分比 return sumSize * 1.0 / fileSize; } private class DownThread extends Thread { // 當前線程的下載位置 private int startPos; // 定義當前線程負責下載的文件大小 private int currentPartSize; // 當前線程須要下載的文件塊 private RandomAccessFile currentPart; // 定義已經該線程已下載的字節數 public int length; public DownThread(int startPos, int currentPartSize,RandomAccessFile currentPart) { this.startPos = startPos; this.currentPartSize = currentPartSize; this.currentPart = currentPart; } @Override public void run() { try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection)url.openConnection(); conn.setConnectTimeout(5 * 1000); conn.setRequestMethod("GET"); conn.setRequestProperty( "Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, " + "application/x-shockwave-flash, application/xaml+xml, " + "application/vnd.ms-xpsdocument, application/x-ms-xbap, " + "application/x-ms-application, application/vnd.ms-excel, " + "application/vnd.ms-powerpoint, application/msword, */*"); conn.setRequestProperty("Accept-Language", "zh-CN"); conn.setRequestProperty("Charset", "UTF-8"); InputStream inStream = conn.getInputStream(); // 跳過startPos個字節,代表該線程只下載本身負責哪部分文件。 inStream.skip(this.startPos); byte[] buffer = new byte[1024]; int hasRead = 0; // 讀取網絡數據,並寫入本地文件 while (length < currentPartSize && (hasRead = inStream.read(buffer)) != -1) { currentPart.write(buffer, 0, hasRead); // 累計該線程下載的總大小 length += hasRead; } currentPart.close(); inStream.close(); } catch (Exception e) { e.printStackTrace(); } } } }

 

測試:DownUtilTest.java

package com.amos;

import com.amos.tool.DownUtil;
import org.omg.PortableServer.THREAD_POLICY_ID;

/**
 * Created by amosli on 14-7-2.
 */
public class DownUtilTest {

    public static void main(String args[]) throws Exception {
        final DownUtil downUtil = new DownUtil("http://mirrors.cnnic.cn/apache/tomcat/tomcat-7/v7.0.54/bin/apache-tomcat-7.0.54.zip", "tomcat-7.0.54.zip", 3);

        downUtil.download();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while(downUtil.getCompleteRate()<1){
                    System.out.println("已完成:"+downUtil.getCompleteRate());
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        }).start();
    }

}

 

結果:

 

下載個人頭像:

  final com.amos.DownUtil downUtil = new com.amos.DownUtil("http://pic.cnitblog.com/avatar/534352/20131215160918.png", "amosli.png", 2);

相關文章
相關標籤/搜索