java-多線程斷點下載

package com.zhong.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * 多線程斷點下載示例
 * 
 * @author zhong
 *
 */
public class MultiThreadDownload {
	
	// 下載所使用的線程數
	private static int threadCount = 3;
	
	// 當前活動的線程數
	private static int activeThread;

	/**
	 * 下載
	 * @param sourcePath
	 * 			--請求資源
	 * @throws IOException
	 */
	public static void download(String sourcePath) throws IOException {
		
		if (sourcePath == null) {

			return;
		}
		// 請求服務器的路徑
	
		String path = sourcePath;
		// 構造URL地址
		URL url = new URL(path);
		// 打開連接
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		// 設置請求超時的時間
		conn.setConnectTimeout(5000);
		// 設置請求方式
		conn.setRequestMethod("GET");
		// 獲取相應碼
		int code = conn.getResponseCode();
		if (code == 200) {// 請求成功
			// 獲取請求數據的長度
			int length = conn.getContentLength();
			// 在客戶端創建一個跟服務器文件大小相同的臨時文件
			RandomAccessFile raf = new RandomAccessFile("setup.exe", "rwd");
			// 指定臨時文件的長度
			raf.setLength(length);
			raf.close();
			// 假設3個線程去下載資源
			// 平均每一個線程要下載的文件的大小
			int blockSize = length / threadCount;
			for (int threadId = 1; threadId <= threadCount; threadId++) {
				// 當前線程下載數據的開始位置
				int startIndex = blockSize * (threadId - 1);
				// 當前線程下載數據的結束位置
				int endIndex = blockSize * threadId - 1;
				// 確定最後一個線程要下載數據的最大位置
				if (threadId == threadCount) {
					endIndex = length;
				}
				// 顯示下載數據的區間
				System.out.println("線程【" + threadId + "】開始下載:" + startIndex + "---->" + endIndex);
				// 開啓下載的子線程
				new DownloadThread(path, threadId, startIndex, endIndex).start();
				
				activeThread++;
				
				System.out.println("當前活動的線程數:" + activeThread);
			}

		} else {// 請求失敗
			System.out.println("服務器異常,下載失敗!");
		}
	}

	public static void main(String[] args) throws Exception {
		download("http://192.168.5.114:8080/sqlite.exe");
	}

	/**
	 * 下載文件的子線程 每一個文件都下載對應的數據
	 * 
	 * @author zhong
	 *
	 */
	public static class DownloadThread extends Thread {

		private String path;
		private int threadId;
		private int startIndex;
		private int endIndex;

		/**
		 * 構造方法
		 * 
		 * @param path
		 *            下載文件的路徑
		 * @param threadId
		 *            下載文件的線程
		 * @param startIndex
		 *            下載文件開始的位置
		 * @param endIndex
		 *            下載文件結束的位置
		 */
		public DownloadThread(String path, int threadId, int startIndex, int endIndex) {
			this.path = path;
			this.threadId = threadId;
			this.startIndex = startIndex;
			this.endIndex = endIndex;
		}

		@Override
		public void run() {
			// 構造URL地址
			try {

				File tempFile = new File(threadId + ".txt");
				// 檢查記錄是否存在,如果存在讀取數據
				if (tempFile.exists()) {
					FileInputStream fis = new FileInputStream(tempFile);
					byte[] temp = new byte[1024];
					int length = fis.read(temp);
					// 讀取到已經下載的位置
					int downloadNewIndex = Integer.parseInt(new String(temp, 0, length));
					// 設置重新開始下載的開始位置
					startIndex = downloadNewIndex;
					fis.close();
					// 顯示真實下載數據的區間
					System.out.println("線程【" + threadId + "】真實開始下載數據區間:" + startIndex + "---->" + endIndex);
				}

				URL url = new URL(path);
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				conn.setConnectTimeout(5000);
				conn.setRequestMethod("GET");
				// 設置請求屬性,請求部分資源
				conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
				int code = conn.getResponseCode();
				if (code == 206) {// 下載部分資源,正常返回的狀態碼爲206
					InputStream is = conn.getInputStream();// 已經設置了請求的位置,所以返回的是對應的部分資源
					// 構建隨機訪問文件
					RandomAccessFile raf = new RandomAccessFile("setup.exe", "rwd");
					// 設置 每一個線程隨機寫文件開始的位置
					raf.seek(startIndex);
					// 開始寫文件
					int len = 0;
					byte[] buffer = new byte[1024];
					// 該線程已經下載數據的長度
					int total = 0;

					while ((len = is.read(buffer)) != -1) {// 讀取輸入流
						// 記錄當前線程已下載數據的長度
						RandomAccessFile file = new RandomAccessFile(threadId + ".txt", "rwd");
						raf.write(buffer, 0, len);// 寫文件
						total += len;// 更新該線程已下載數據的總長度
						System.out.println("線程【" + threadId + "】已下載數據:" + (total + startIndex));
						// 將已下載數據的位置記錄寫入到文件
						file.write((startIndex + total + "").getBytes());
						file.close();
					}
					is.close();
					raf.close();
					// 提示下載完畢
					System.out.println("線程【" + threadId + "】下載完畢");
				}
			} catch (Exception e) {
				e.printStackTrace();
				System.out.println("線程【" + threadId + "】下載出現異常!!");
			} finally {
				
				// 活動的線程數減少
				activeThread--;
				if (activeThread == 0) {
					for (int i = 1; i <= threadCount; i++) {
						File tempFile = new File(i + ".txt");
						tempFile.delete();
					}
					System.out.println("下載完畢,已清除全部臨時文件");
				}
			}

		}
	}
}

 

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