首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 多線程斷點續傳下載器實現:HttpURLConnection、Range 請求、CountDownLatch 與臨時文件合併

     編輯:Java 相關知識

本文實現一個支援多線程並發下載、斷點續傳、超時重連的 HTTP 下載器,核心技術點包括 HttpURLConnection Range 請求、ExecutorService 線程池、CountDownLatch 同步、臨時文件分段存儲與合併。

一、整體架構:三階段流程

  1. 斷點分析:掃描臨時文件,確定各線程已下載位移
  2. 多線程下載:按範圍並發請求,寫入獨立臨時文件
  3. 文件合併:按線程順序合併臨時文件為目標文件,清理臨時文件

二、核心代碼解析

主下載流程

public String download(String urlStr, String charset) {
 // 1. 解析文件名、建立連接、獲取 contentLength
 // 2. 計算每線程平均長度 threadLength = contentLength / threadNum
 // 3. 斷點分析:startPos = setThreadBreakpoint(...)
 // 4. 建立線程池
 ExecutorService exec = Executors.newCachedThreadPool();
 CountDownLatch latch = new CountDownLatch(threadNum);
 
 for (int i = 0; i < threadNum; i++) {
 startPos[i] += threadLength * i; // 全局起始位移
 long endPos = (i == threadNum - 1) ? contentLength : threadLength * (i + 1) - 1;
 ChildThread t = new ChildThread(this, latch, i, startPos[i], endPos);
 childThreads[i] = t;
 exec.execute(t);
 }
 
 latch.await(); // 阻塞等待所有線程完成
 exec.shutdown();
 tempFileToTargetFile(childThreads); // 合併文件
}

子線程 ChildThread:Range 請求、超時重連、進度持久化

public class ChildThread extends Thread {
 // 構造器:建立臨時文件 fileName_" + id
 public void run() {
 BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(tempFile, true));
 long downloaded = 0;
 
 for (;;) { // 無限重試循環
 startPosition += downloaded; // 斷點續傳核心:基於已下載量調整起始位置
 
 HttpURLConnection con = (HttpURLConnection) url.openConnection();
 setHeader(con); // 偽裝 User-Agent、Referer 等
 con.setConnectTimeout(10000);
 con.setReadTimeout(10000);
 con.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);
 
 // 狀態碼檢查:僅接受 200/206
 if (con.getResponseCode() != HTTP_OK && con.getResponseCode() != HTTP_PARTIAL) {
 // 錯誤處理、countDown、break
 }
 
 InputStream in = con.getInputStream();
 byte[] buf = new byte[1024];
 int len;
 while ((len = in.read(buf)) != -1) {
 out.write(buf, 0, len);
 downloaded += len;
 if (downloaded % 5000 == 0) out.flush(); // 定期持久化
 }
 
 if (downloaded >= (endPosition - startPosition + 1)) break; // 完成
 out.flush(); out.close(); in.close(); con.disconnect();
 latch.countDown(); break;
 } catch (IOException e) {
 out.flush(); // 異常時也要持久化已下載數據
 TimeUnit.SECONDS.sleep(getSleepSeconds()); // 等待重試
 }
 }
 }
}

關鍵技術細節

技術點實現要點
Range 請求con.setRequestProperty("Range", "bytes=start-end"),支援 206 Partial Content
偽裝 HeaderUser-Agent 模擬 Firefox/Ubuntu,Referer 指定來源頁,繞過防盜鏈
超時控制setConnectTimeout / setReadTimeout 10s,觸發 SocketTimeoutException 進入重試
狀態碼校驗僅接受 200 OK、206 Partial Content,其他視為錯誤終止線程
定期 Flush每 5KB 刷盤,異常捕獲塊同步 Flush,確保斷點數據不丟失
斷點持久化臨時文件命名 filename_id,文件大小即為已下載位移
線程同步CountDownLatch 初始計數 = 線程數,每線程完成 countDown(),主線程 await()

文件合併

private void tempFileToTargetFile(ChildThread[] threads) {
 BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(targetFile));
 for (int i = 0; i < threadNum; i++) {
 BufferedInputStream in = new BufferedInputStream(new FileInputStream(threads[i].tempFile));
 byte[] buf = new byte[1024];
 int len, count = 0;
 while ((len = in.read(buf)) != -1) {
 out.write(buf, 0, len);
 if (++count % 5000 == 0) out.flush();
 }
 in.close();
 if (threads[i].status == STATUS_HAS_FINISHED) threads[i].tempFile.delete();
 }
 out.flush(); out.close();
}

斷點掃描邏輯

private long[] setThreadBreakpoint(String dir, String fileName, long contentLength, long[] startPos) {
 File target = new File(dir, fileName);
 if (target.exists() && target.length() < contentLength) { // 未完成,續傳
 File[] temps = new File(dir).listFiles();
 for (File f : temps) {
 if (f.getName().startsWith(fileName + "_") && f.length() > 0) {
 int idx = Integer.parseInt(f.getName().substring(f.getName().lastIndexOf("_") + 1));
 startPos[idx] = f.length(); // 文件大小 = 已下載位移
 }
 }
 } else if (!target.exists()) {
 target.createNewFile(); // 新任務
 }
 return startPos;
}

三、測試驗證

public class DownloadStartup {
 public static void main(String[] args) {
 DownloadTask dm = new DownloadTask();
 dm.setSleepSeconds(5); // 重試間隔 5s
 dm.download("https://apache.freelamp.com/velocity/tools/1.4/velocity-tools-1.4.zip", "utf-8");
 }
}

四、生產級擴展建議

  • 支援 HTTPS、代理、Cookie 持久化
  • 實現下載進度回調、速度限制、動態調整線程數
  • 引入校驗和(MD5/SHA1)驗證文件完整性
  • 支援磁碟空間不足、網絡斷開等異常的優雅降級
  • 使用 RandomAccessFile 替換臨時文件合併,減少磁碟 I/O

此實現展示了 Java NIO.2 之前時代,利用 BIO + 多線程 + Range 請求構建健壯下載器的經典模式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved