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("下載完畢,已清除全部臨時文件");
}
}
}
}
}