斷點續傳是下載工具的核心功能,本文系統講解 HTTP 斷點續傳的協議原理、Java 關鍵 API 使用,並提供一個基於多線程分片下載的完整框架級實現代碼(含 6 個核心類),適合作為下載模塊參考架構。
GET /down.zip HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: */*
Connection: Keep-Alive
--- 響應 ---
HTTP/1.1 200 OK
Content-Length: 106786028
Accept-Ranges: bytes
Content-Type: application/octet-stream
Last-Modified: Mon, 30 Apr 2001 12:56:11 GMT
GET /down.zip HTTP/1.0
User-Agent: NetFox
Range: bytes=2000070- # 從第 2000070 字節開始
Accept: */*
--- 響應 ---
HTTP/1.1 206 Partial Content
Content-Length: 104785958
Content-Range: bytes 2000070-106786027/106786028
Content-Type: application/octet-stream
關鍵差異:請求添加 Range: bytes=start-,響應狀態碼變為 206,新增 Content-Range 頭指示實際返回範圍。
URL url = new URL("https://www.example.com/down.zip");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("User-Agent", "NetFox");
conn.setRequestProperty("Range", "bytes=2000070-"); // 關鍵設置
InputStream in = conn.getInputStream(); // 獲取斷點數據流
RandomAccessFile raf = new RandomAccessFile("down.zip", "rw");
raf.seek(2000070); // 定位寫入位置
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
raf.write(buf, 0, len);
}
SiteFileFetch (主控線程)
│
├─ 管理 SiteInfoBean (任務配置)
├─ 動態分割文件 → 創建 FileSplitterFetch[] (工作線程)
├─ 監控進度、持久化斷點信息 (.info 文件)
└─ 協調完成/失敗/停止狀態
FileSplitterFetch (分片下載線程)
│
├─ 負責 [startPos, endPos] 區間下載
├─ 發送 Range 請求、寫入文件
├─ 異常自動重試(從 curPos 繼續)
└─ 通過 FileAccessI 寫入文件
FileAccessI (文件訪問接口)
└─ 封裝 RandomAccessFile,提供線程安全 write()
SiteInfoBean (任務配置 Bean)
└─ URL、保存路徑、文件名、分片數
Utility (工具類)
└─ sleep、log 等通用方法
TestMethod (測試入口)
public class SiteFileFetch extends Thread {
// ... 字段定義 ...
public void run() {
try {
if (bFirst) {
nFileLength = getFileSize(); // HEAD 請求獲取文件大小
// 等分文件計算 startPos/endPos 數組
}
// 啟動所有分片線程
fileSplitterFetch = new FileSplitterFetch[nStartPos.length];
for (int i = 0; i < nStartPos.length; i++) {
fileSplitterFetch[i] = new FileSplitterFetch(...);
fileSplitterFetch[i].start();
}
// 監控循環:持久化進度、檢查完成
while (!bStop) {
write_nPos(); // 保存各線程當前位置到 .info 文件
Utility.sleep(500);
// 檢查所有線程是否完成
}
} catch (Exception e) { e.printStackTrace(); }
}
// 斷點持久化:保存每個線程的 startPos/endPos
private void write_nPos() { /* DataOutputStream 寫入 .info 文件 */ }
private void read_nPos() { /* DataInputStream 讀取 .info 文件恢復 */ }
}
public class FileSplitterFetch extends Thread {
public void run() {
while (nStartPos < nEndPos && !bStop) {
try {
URL url = new URL(sURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Range", "bytes=" + nStartPos + "-");
InputStream in = conn.getInputStream();
byte[] buf = new byte[1024];
int nRead;
while ((nRead = in.read(buf)) > 0 && nStartPos < nEndPos && !bStop) {
nStartPos += fileAccessI.write(buf, 0, nRead); // 寫入並更新位置
}
bDownOver = true;
} catch (Exception e) { e.printStackTrace(); }
}
}
}
public class FileAccessI implements Serializable {
private RandomAccessFile raf;
private long pos;
public synchronized int write(byte[] b, int off, int len) {
try {
raf.write(b, off, len);
return len;
} catch (IOException e) { e.printStackTrace(); return -1; }
}
}
| 維度 | 現狀問題 | 優化方案 |
|---|---|---|
| 連接管理 | 每線程獨立創建 HttpURLConnection | 使用連接池、Keep-Alive、HttpClient 復用連接 |
| 進度持久化 | 頻繁寫入 .info 文件,性能差 | 內存緩衝 + 定時刷盤、或用 SQLite/LevelDB |
| 錯誤處理 | 單純 printStackTrace、無限重試 | 分類異常、指數退避重試、熔斷機制 |
| 校驗機制 | 無文件完整性校驗 | 下載完成後驗證 MD5/SHA256、ETag/Last-Modified |
| 速度控制 | 無限速、佔滿帶寬 | 令牌桶/漏桶算法實現下載限速 |
| 暫停/恢復 | 僅支持停止標誌 | 暴露 pause/resume 接口、狀態機管理 |
| 多文件/隊列 | 單任務設計 | 引入下載隊列、優先級調度、並發數控制 |
本文從 HTTP 協議層面徹底講解斷點續傳原理,展示 Java 核心 API(HttpURLConnection + RandomAccessFile)實現要點,並提供一個完整的多線程分片下載框架代碼結構。理解這些基礎原理對於排查下載問題、優化性能、擴展功能至關重要。實際項目中建議基於成熟庫二次開發,專注於業務邏輯而非重複造輪子。