多線程斷點續傳--java

以前看過一個前輩寫的斷點續傳的文章,記得當時沒看懂,就扔那了。昨天翻了出來,自己也仿照寫了一個,不過感覺沒人家寫的好,有點亂,希望大家能來批評、指正,給點意見!

 

    功能很簡單,就是啓動多個線程分別從給定的地址下載數據,用RandomAccessFile寫到目標文件。實現思路是:

    1、獲得連接的長度(即要下載的文件大小),除以設定的線程數,即得到每個線程要下載的大小。

    2、記錄臨時文件,文件中記錄每個線程的編號(id),該線程要下載的起始位置、終止位置和當前位置(當前位置在首次下載時與起始位置相同)。

    3、啓動具體執行下載任務的線程,並等待其結束。

    4、下載完成,刪除臨時文件。

 

代碼如下:

主線程及測試的main方法

Java代碼  收藏代碼
  1. package com.why.download.test;  
  2.   
  3. import java.io.DataOutputStream;  
  4. import java.io.File;  
  5. import java.io.FileOutputStream;  
  6. import java.io.IOException;  
  7. import java.math.BigDecimal;  
  8. import java.net.HttpURLConnection;  
  9. import java.net.URL;  
  10. import java.net.URLConnection;  
  11. import java.util.UUID;  
  12. import java.util.concurrent.CountDownLatch;  
  13. import java.util.concurrent.ExecutorService;  
  14. import java.util.concurrent.Executors;  
  15.   
  16. /** 
  17.  *  
  18.  * @author why 
  19.  * 
  20.  */  
  21. public class DownLoad {  
  22.     //文件目錄、文件名  
  23.     public String fileDir = "E:/MyDownLoad";  
  24.     public String fileName;  
  25.     //超時重連時間  
  26.     public long reconnectTime = 5;  
  27.     //線程數  
  28.     private int poolSize = 5;  
  29.     //每個線程的緩衝區大小  
  30.     public int bufferSize = 1024;  
  31.     //url地址  
  32.     private String urlLocation = null;  
  33.       
  34.     public DownLoad(){}  
  35.     public DownLoad(String url){  
  36.         this.urlLocation = url;  
  37.     }  
  38.     public void downLoad(){  
  39.         if(this.urlLocation == null || "".equals(this.urlLocation))return;  
  40.         downLoad(this.urlLocation);  
  41.     }  
  42.     public void downLoad(String urlLocation){  
  43.         File file = null;  
  44.         File tempFile = null;  
  45.         CountDownLatch latch;  
  46.         URL url = null;  
  47.         ExecutorService pool = Executors.newCachedThreadPool();  
  48.         long contentLength = 0;  
  49.         long threadLength = 0;  
  50.         try {  
  51.             //如果未指定名稱,則從url中獲得下載的文件格式與名字  
  52.             if(fileName == null || "".equals(fileName)){  
  53.                 this.fileName = urlLocation.substring(urlLocation.lastIndexOf("/") + 1,  
  54.                         urlLocation.lastIndexOf("?") > 0 ? urlLocation.lastIndexOf("?")  
  55.                                 : urlLocation.length());  
  56.                 if ("".equalsIgnoreCase(this.fileName)) {  
  57.                     this.fileName = UUID.randomUUID().toString();  
  58.                 }  
  59.             }  
  60.             new File(fileDir).mkdirs();  
  61.             file = new File(fileDir + File.separator + fileName);  
  62.             tempFile = new File(fileDir + File.separator + fileName + "_temp");  
  63.               
  64.             url = new URL(urlLocation);  
  65.             HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  66.             setHeader(conn);  
  67.             //得到content的長度  
  68.             contentLength = conn.getContentLength();  
  69.               
  70.             System.out.println("total length=" + contentLength);  
  71.               
  72.             //把context分爲poolSize段,計算每段的長度。  
  73. //          threadLength = contentLength / this.poolSize;  
  74.             BigDecimal b1 = new BigDecimal(Double.toString(contentLength));  
  75.             BigDecimal b2 = new BigDecimal(Double.toString(this.poolSize));  
  76.             threadLength = b1.divide(b2, 0, BigDecimal.ROUND_HALF_UP).longValue();  
  77.               
  78.             if(file.exists() && tempFile.exists()){  
  79.                 //如果文件已存在,根據臨時文件中記載的線程數量,繼續上次的任務  
  80.                 latch = new CountDownLatch((int)tempFile.length()/28);  
  81.                 for(int i=0;i<tempFile.length()/28;i++){  
  82.                     pool.submit(new DownLoadTask(file, tempFile, url, i+1,latch,reconnectTime,bufferSize));  
  83.                 }  
  84.             }else{  
  85.                 //如果下載的目標文件不存在,則創建新文件  
  86.                 latch = new CountDownLatch(poolSize);  
  87.                 file.createNewFile();  
  88.                 tempFile.createNewFile();  
  89.                 DataOutputStream os = new DataOutputStream(new FileOutputStream(tempFile));  
  90.                 for(int i=0;i<this.poolSize;i++){  
  91.                     os.writeInt(i+1);  
  92.                     os.writeLong(i*threadLength);  
  93.                     if(i==this.poolSize-1){//最後一個線程的結束位置應爲文件末端  
  94.                         os.writeLong(contentLength);  
  95.                     }else{  
  96.                         os.writeLong((i+1)*threadLength);  
  97.                     }  
  98.                     os.writeLong(i*threadLength);  
  99.                     pool.submit(new DownLoadTask(file, tempFile, url, i+1,latch,reconnectTime,bufferSize));  
  100.                 }  
  101.                 os.close();  
  102.             }  
  103.             //等待下載任務完成  
  104.             latch.await();  
  105.             //刪除臨時文件  
  106.             tempFile.delete();  
  107.         } catch (IOException e) {  
  108.             e.printStackTrace();  
  109.         } catch (InterruptedException e) {  
  110.             e.printStackTrace();  
  111.         } finally{  
  112.             pool.shutdown();   
  113.         }  
  114.     }  
  115.       
  116.     private void setHeader(URLConnection conn) {  
  117.         conn.setRequestProperty("User-Agent",  
  118.                         "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3");  
  119.         conn.setRequestProperty("Accept-Language""en-us,en;q=0.7,zh-cn;q=0.3");  
  120.         conn.setRequestProperty("Accept-Encoding""aa");  
  121.         conn.setRequestProperty("Accept-Charset","ISO-8859-1,utf-8;q=0.7,*;q=0.7");  
  122.         conn.setRequestProperty("Keep-Alive""300");  
  123.         conn.setRequestProperty("Connection""keep-alive");  
  124.         conn.setRequestProperty("If-Modified-Since""Fri, 02 Jan 2009 17:00:05 GMT");  
  125.         conn.setRequestProperty("If-None-Match""\"1261d8-4290-df64d224\"");  
  126.         conn.setRequestProperty("Cache-Control""max-age=0");  
  127.         conn.setRequestProperty("Referer","http://www.skycn.com/soft/14857.html");  
  128.     }  
  129.   
  130.       
  131.     public String getFileDir() {  
  132.         return fileDir;  
  133.     }  
  134.     public void setFileDir(String fileDir) {  
  135.         this.fileDir = fileDir;  
  136.     }  
  137.     public String getFileName() {  
  138.         return fileName;  
  139.     }  
  140.     public void setFileName(String fileName) {  
  141.         this.fileName = fileName;  
  142.     }  
  143.     public long getReconnectTime() {  
  144.         return reconnectTime;  
  145.     }  
  146.     public void setReconnectTime(long reconnectTime) {  
  147.         this.reconnectTime = reconnectTime;  
  148.     }  
  149.     public int getPoolSize() {  
  150.         return poolSize;  
  151.     }  
  152.     public void setPoolSize(int poolSize) {  
  153.         this.poolSize = poolSize;  
  154.     }  
  155.     public int getBufferSize() {  
  156.         return bufferSize;  
  157.     }  
  158.     public void setBufferSize(int bufferSize) {  
  159.         this.bufferSize = bufferSize;  
  160.     }  
  161.     /** 
  162.      * @param args 
  163.      */  
  164.     public static void main(String[] args) {  
  165.         DownLoad dl = new DownLoad();  
  166.         dl.setFileDir("E:/MyDownLoad/music/");  
  167.         dl.setFileName("大笑江湖.mp3");  
  168.         dl.setPoolSize(20);  
  169.         long beginTime = System.currentTimeMillis();  
  170.         dl.downLoad("http://mh.163k.com/UploadFile/video/2010/12-13/201012131213448942190.mp3");  
  171.         long endTime = System.currentTimeMillis();  
  172.         BigDecimal b1 = new BigDecimal(endTime - beginTime);  
  173.         BigDecimal b2 = new BigDecimal(1000);  
  174.         double cost = b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP).doubleValue();  
  175.         System.out.println("Time cost:" + cost + "s");  
  176.     }  
  177.   
  178. }  
 

 

執行下載任務的線程:

Java代碼  收藏代碼
  1. package com.why.download.test;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileNotFoundException;  
  5. import java.io.IOException;  
  6. import java.io.InputStream;  
  7. import java.io.RandomAccessFile;  
  8. import java.net.HttpURLConnection;  
  9. import java.net.URL;  
  10. import java.net.URLConnection;  
  11. import java.util.concurrent.Callable;  
  12. import java.util.concurrent.CountDownLatch;  
  13. import java.util.concurrent.TimeUnit;  
  14.   
  15. /** 
  16.  *  
  17.  * @author why 
  18.  * 
  19.  */  
  20. public class DownLoadTask implements Callable<String>{  
  21.     //超時重連時間  
  22.     private long reconnectTime = 5;  
  23.     //緩衝區大小  
  24.     private int bufferSize = 1024;  
  25.       
  26.     private CountDownLatch latch;  
  27.     private RandomAccessFile file = null;  
  28.     private RandomAccessFile tempFile = null;  
  29.     private URL url = null;  
  30.     private int id;  
  31.     private long startPosition;  
  32.     private long endPosition;  
  33.     private long currentPosition ;  
  34.       
  35.     public DownLoadTask(File file,File tempFile,URL url,int id,CountDownLatch latch,long reconnectTime,int bufferSize){  
  36.         try {  
  37.             this.file = new RandomAccessFile(file, "rw");  
  38.             this.tempFile = new RandomAccessFile(tempFile, "rw");  
  39.         } catch (FileNotFoundException e) {  
  40.             e.printStackTrace();  
  41.         }  
  42.         this.url = url;  
  43.         this.id = id;  
  44.         this.latch = latch;  
  45.     }  
  46.       
  47.     public String call(){  
  48.           
  49.         try {  
  50.             tempFile.seek((id-1)*28);  
  51.             tempFile.readInt();  
  52.             this.startPosition = tempFile.readLong();  
  53.             this.endPosition = tempFile.readLong();  
  54.             this.currentPosition = tempFile.readLong();  
  55.         } catch (IOException e) {  
  56.             e.printStackTrace();  
  57.         }  
  58.           
  59.         System.out.println("Thread " + id + " begin!");  
  60.           
  61.         HttpURLConnection conn = null;  
  62.         InputStream inputStream = null;  
  63.   
  64.         while(true){  
  65.             try {  
  66.                 tempFile.seek(id*28 - 8);  
  67.                 // 打開URLConnection  
  68.                 conn = (HttpURLConnection) this.url.openConnection();  
  69.                 setHeader(conn);  
  70.                 // 設置連接超時時間爲10000ms  
  71.                 conn.setConnectTimeout(10000);  
  72.                 // 設置讀取數據超時時間爲10000ms  
  73.                 conn.setReadTimeout(10000);  
  74.   
  75.                 if (currentPosition < endPosition) {  
  76.                     // 設置下載數據的起止區間  
  77.                     conn.setRequestProperty("Range""bytes=" + currentPosition + "-" + endPosition);  
  78.                       
  79.                     System.out.println("Thread " + id + " startPosition=" + startPosition   
  80.                             + ",endPosition=" + endPosition + ",currentPosition=" + currentPosition);  
  81.   
  82.                     file.seek(currentPosition);  
  83.   
  84.                     // 判斷http status是否爲HTTP/1.1 206 Partial Content或者200 OK  
  85.                     // 如果不是以上兩種狀態,把status改爲STATUS_HTTPSTATUS_ERROR  
  86.                     if (conn.getResponseCode() != HttpURLConnection.HTTP_OK  
  87.                             && conn.getResponseCode() != HttpURLConnection.HTTP_PARTIAL) {  
  88.                         System.out.println("Thread " + id + ": code = " + conn.getResponseCode() + ", status = " + conn.getResponseMessage());  
  89.                         file.close();  
  90.                         conn.disconnect();  
  91.                         System.out.println("Thread " + id + " finished.");  
  92.                         break;  
  93.                     }  
  94.   
  95.                     inputStream = conn.getInputStream();  
  96.                     int len = 0;  
  97.                     byte[] b = new byte[bufferSize];  
  98.                     while ((len = inputStream.read(b)) != -1) {  
  99.                         file.write(b, 0, len);  
  100.   
  101.                         currentPosition += len;  
  102.                         // set tempFile now position  
  103.                         tempFile.seek(id*28 - 8);  
  104.                         tempFile.writeLong(currentPosition);  
  105.                     }  
  106.   
  107.                     file.close();  
  108.                     tempFile.close();  
  109.                     inputStream.close();  
  110.                     conn.disconnect();  
  111.                 }  
  112.   
  113.                 System.out.println("Thread " + id + " finished.");  
  114.                 break;  
  115.             } catch (IOException e) {  
  116.                 try {  
  117.                     TimeUnit.SECONDS.sleep(getReconnectTime());  
  118.                 } catch (InterruptedException e1) {  
  119.                     e1.printStackTrace();  
  120.                 }  
  121.                 continue;  
  122.             }  
  123.         }  
  124.         latch.countDown();  
  125.         return "finish";  
  126.     }  
  127.       
  128.     private void setHeader(URLConnection conn) {  
  129.         conn.setRequestProperty("User-Agent",  
  130.                         "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3");  
  131.         conn.setRequestProperty("Accept-Language""en-us,en;q=0.7,zh-cn;q=0.3");  
  132.         conn.setRequestProperty("Accept-Encoding""aa");  
  133.         conn.setRequestProperty("Accept-Charset","ISO-8859-1,utf-8;q=0.7,*;q=0.7");  
  134.         conn.setRequestProperty("Keep-Alive""300");  
  135.         conn.setRequestProperty("Connection""keep-alive");  
  136.         conn.setRequestProperty("If-Modified-Since""Fri, 02 Jan 2009 17:00:05 GMT");  
  137.         conn.setRequestProperty("If-None-Match""\"1261d8-4290-df64d224\"");  
  138.         conn.setRequestProperty("Cache-Control""max-age=0");  
  139.         conn.setRequestProperty("Referer","http://www.skycn.com/soft/14857.html");  
  140.     }  
  141.   
  142.     public long getReconnectTime() {  
  143.         return reconnectTime;  
  144.     }  
  145.   
  146.     public void setReconnectTime(long reconnectTime) {  
  147.         this.reconnectTime = reconnectTime;  
  148.     }  
  149.   
  150.     public int getBufferSize() {  
  151.         return bufferSize;  
  152.     }  
  153.   
  154.     public void setBufferSize(int bufferSize) {  
  155.         this.bufferSize = bufferSize;  
  156.     }  
  157.   
  158.       
  159. }  
 

    最近一直在測試,寫文檔,好久沒敲代碼了。手有點癢,寫着玩的,代碼寫的不是很工整,純屬娛樂!


轉載自:http://wuhongyu.iteye.com/blog/869109

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章