多線程下載原理及核心代碼

原文出自:http://blog.csdn.net/howlaa/article/details/21875991


假如我們把一個服務器上的文件看作是一個水缸裏的水的話,那麼多線程下載就相當於從水缸上打了多個小孔,然後塞進去小管道進行抽水。呵呵,也許這個比喻不夠準確。多線程下載大致可分爲以下幾個步驟:

一、首先在本地創建一個與服務器文件大小相同的臨時文件(這個很好理解,如果我想下個2G的電影,我得給先在本地佔用2G的空間,不然不能下着下着沒空間了是吧)。

二、計算分配幾個線程去下載服務器上的資源,知道每個線程下載文件的起始位置。

  那麼這個起始位置怎麼計算呢?

文件長度/線程個數= 每個線程下載文件的大小。那麼

線程1下載的位置:0~每個線程下載文件的大小-1.

線程2:以此類推

那麼就是i線程的下載起始位置: (i-1)*每個線程下載文件的大小

三、開啓多個線程,每一個線程下載對應位置的文件。

四、如果所有的線程都把自己的數據下載完畢了,服務器上的資源就被下載到本地了。

五、當文件都下載到本地了,那麼還有一個文件就是把各個線程下載的文件如何串起來。那麼就要利用到一個類:RandomAccessFile 隨機文件訪問類。

代碼如下:

  1. import java.io.InputStream;  
  2. import java.io.RandomAccessFile;  
  3. import java.net.HttpURLConnection;  
  4. import java.net.URL;  
  5.   
  6.   
  7. public class Demo {  
  8.     public static int threadCount = 3;  
  9.     /** 
  10.      * @param args 
  11.      */  
  12.     public static void main(String[] args) throws Exception{  
  13.         //連接服務器,獲取文件長度,在本地創建一個大小和服務器一樣大的臨時文件  
  14.         String path ="http://192.168.1.100:8080/360.exe";  
  15.         URL url = new URL(path);  
  16.         HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  17.         conn.setConnectTimeout(5000);  
  18.         conn.setRequestMethod("GET");  
  19.         int code = conn.getResponseCode();  
  20.         if(code==200){  
  21.             //服務器返回的數據的長度,實際上就是文件的長度  
  22.             int length = conn.getContentLength();  
  23.             System.out.println("文件總長度:"+length);  
  24.             RandomAccessFile raf = new RandomAccessFile("setup.exe""rwd");  
  25.             //指定創建的文件的長度  
  26.             raf.setLength(length);  
  27.             raf.close();  
  28.             //在客戶端本地   
  29.             //假設3個線程去下載資源  
  30.             //平均每一個線程下載的文件的大小。  
  31.             int blockSize = length / threadCount;  
  32.               
  33.             for(int threadId=1;threadId<=threadCount;++threadId){  
  34.                 //第一個線程下載的開始位置  
  35.                 int startIndex = (threadId-1)*blockSize;  
  36.                 int endIndex = blockSize - 1;  
  37.                 if(threadId==threadCount){  
  38.                     //最後一個線程下載的長度稍微長一點  
  39.                     endIndex = length;  
  40.                 }  
  41.                 System.out.println("線程:"+threadId+"下載:--"+startIndex+"-->"+endIndex);  
  42.                 new DownLoadThread(threadId, startIndex, endIndex, path).start();  
  43.             }  
  44.         }else{  
  45.             System.out.println("訪問錯誤");  
  46.         }  
  47.           
  48.     }  
  49.       
  50.     /** 
  51.      * 下載文件的子線程,每個線程下載對應的文件 
  52.      * 
  53.      */  
  54.     public static class DownLoadThread extends Thread{  
  55.         private int threadId;  
  56.         private int startIndex;  
  57.         private int endIndex;  
  58.         private String path;  
  59.         /** 
  60.          * @param threadId線程ID 
  61.          * @param startIndex 
  62.          * @param endIndex 
  63.          * @param path 下載文件在服務器上的路徑 
  64.          */  
  65.         public DownLoadThread(int threadId, int startIndex, int endIndex,String path) {  
  66.             this.threadId = threadId;  
  67.             this.startIndex = startIndex;  
  68.             this.endIndex = endIndex;  
  69.             this.path = path;  
  70.         }  
  71.   
  72.         @Override  
  73.         public void run() {  
  74.             try{  
  75.                 URL url = new URL(path);  
  76.                 HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  77.                 conn.setRequestMethod("GET");  
  78.                 //很重要:請求服務器下載部分的文件的指定的位置:  
  79.                 conn.setRequestProperty("Range""bytes="+startIndex+"-"+endIndex);  
  80.                 conn.setConnectTimeout(5000);  
  81.                 int code  = conn.getResponseCode();//從服務器請求全部資源 200ok ,如果請求部分資源 206 ok  
  82.                 System.out.println("code="+code);  
  83.                   
  84.                 InputStream is = conn.getInputStream();//返回資源  
  85.                 RandomAccessFile raf = new RandomAccessFile("setup.exe""rwd");  
  86.                 //隨機寫文件的時候從哪個位置開始寫  
  87.                 raf.seek(startIndex);//定位文件  
  88.                   
  89.                 int len =0;  
  90.                 byte[] buffer = new byte[1024];  
  91.                 while((len = is.read(buffer)) != -1){  
  92.                     raf.write(buffer,0,len);  
  93.                 }  
  94.                 is.close();  
  95.                 raf.close();  
  96.                 System.out.println("線程"+threadId+"下載完畢");  
  97.                   
  98.             }catch(Exception e){  
  99.                 e.printStackTrace();  
  100.             }  
  101.         }  
  102.           
  103.     }  
  104.   

發佈了120 篇原創文章 · 獲贊 57 · 訪問量 28萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章