1. 程式人生 > >java 多執行緒檔案下載,斷點續傳

java 多執行緒檔案下載,斷點續傳

1,把阿里旺旺傳到伺服器上

2,分3個執行緒,分別下載不同位置的檔案

3,用3個檔案記錄每次下載的位置,停止後再次下載時,直接從已下載的位置開始繼續下載,當檔案下載完成後刪除記錄的檔案


測試成功,下面是實現程式碼:

package com.zhuyu.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * 多執行緒下載檔案
 * @author zhuyu
 * 支援斷點續傳
 */
public class MultiDownload {
	//執行緒數量
	public static int threadCount = 3;
	//記錄執行緒下載檔案標識,所有執行緒下載完成則檔案下載完成
	public static int runningThread = threadCount;
	
	public static void main(String[] args) throws MalformedURLException {		
		//1.獲取檔案大小,建立一個和伺服器檔案對應的臨時檔案
		//2.計算分配幾個執行緒下載伺服器資源,知道每個執行緒下載檔案的位置
		//3.開啟多執行緒  開始位置:(執行緒id-1)* 每一塊大小   結束位置:(執行緒id* 每一塊大小)-1
		String path = "http://192.168.20.117:8888/AliIM2014.exe";
		URL url = new URL(path);
		try {
			String newFileName = path.substring(path.lastIndexOf("/") + 1);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setConnectTimeout(5000);
			conn.setRequestMethod("GET");
			int code = conn.getResponseCode();
			if(code == 200){
				//伺服器檔案總大小
				int length = conn.getContentLength();
				int blockSize = length / threadCount; //每一塊的大小
				//在客戶端本地建立一個跟服務端大小一致的臨時檔案
				RandomAccessFile raf = new RandomAccessFile(newFileName,"rwd");
				raf.setLength(length);
				raf.close();
				
				for(int threadId=1;threadId <= threadCount ; threadId ++){
					int startIndex = (threadId - 1) * blockSize;
					int endIndex = (threadId * blockSize) - 1;
					if(threadId == threadCount){ //如果執行緒是最後一個,則結束位置為最後位置
						endIndex = length ;
					}
					//下載檔案
					new DownloadFileThread(path, startIndex, endIndex, threadId){}.start();
				}
				
			}else{
				System.out.println("請求失敗");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static class DownloadFileThread extends Thread {
		private String filepath;
		private int startIndex;
		private int endIndex;
		private int threadId;
		private String newFileName;

		public DownloadFileThread(String filepath, int startIndex, int endIndex,int threadId) {
			this.filepath = filepath;
			this.startIndex = startIndex;
			this.endIndex = endIndex;
			this.threadId = threadId;
			this.newFileName = filepath.substring(filepath.lastIndexOf("/") + 1);
		}

		@Override
		public void run() {
			try {
				//為支援斷點續傳,終止或異常發生時,儲存檔案下載的最後位置,下次繼續下載時讀取出來,從上次結束位置開始下載
				//判斷是否存在已下載的檔案記錄,如果存在則開始位置
				File file = new File(threadId + ".txt");
				if(file.exists() && file.length() > 0){
					String tempFileContent = txt2String(file);
					startIndex = Integer.parseInt(tempFileContent);
				}
				
				URL url = new URL(filepath);
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);//請求資源的範圍,下載部分檔案,需指定開始-結束位置
				conn.setConnectTimeout(5000);
				conn.setRequestMethod("GET");
				int code = conn.getResponseCode();//200是成功,206也是成功,分割一個檔案返回206
				InputStream is = conn.getInputStream(); //由於設定了請求的位置,返回的是當前位置對應的輸入流
				
				RandomAccessFile raf = new RandomAccessFile(newFileName,"rwd");
				raf.seek(startIndex);
				int len = 0 , total = 0;
				byte[] buffer = new byte[1024];
				while((len = is.read(buffer)) != -1){//沒有讀到檔案末尾則繼續寫
					raf.write(buffer, 0, len);
					total += len;
					RandomAccessFile info = new RandomAccessFile(threadId + ".txt","rwd");
					info.write((total + startIndex + "").getBytes());//把下載到最後的位置儲存寫到檔案中
					info.close();
				}
				is.close();
				raf.close();
				//System.out.println("執行緒:"+threadId +"------下載完畢");
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally{
				runningThread --;
				//當執行緒執行完後,表示檔案下載完畢,清除臨時檔案記錄
				if(runningThread == 0){
					for(int i=1;i <= threadCount; i++){
						File file = new File(i+".txt");
						if(file.exists()){
							file.delete();
						}
					}
				}
			}
		}
		
		/**
	     * 讀取txt檔案的內容
	     * @param file 想要讀取的檔案物件
	     * @return 返回檔案內容
	     */
	    public static String txt2String(File file){
	        StringBuilder result = new StringBuilder();
	        try{
	            BufferedReader br = new BufferedReader(new FileReader(file));//構造一個BufferedReader類來讀取檔案
	            String s = null;
	            while((s = br.readLine())!=null){//使用readLine方法,一次讀一行
	                result.append(s);
	            }
	            br.close();    
	        }catch(Exception e){
	            e.printStackTrace();
	        }
	        return result.toString();
	    }
		
	}

}