功能特色
- 支援 HTTP Range 請求 實現斷點續傳。
- 多線程並發下載 同一檔案(示例啟動兩個執行緒下載同一 URL)。
- 本地檔案大小與遠端
Content-Length 比對,自動判斷是否續傳或已完成。
- 獨立進度顯示執行緒,每 5 秒輸出下載百分比。
- 使用
RandomAccessFile 定位寫入位置。
核心類別結構
Download 主類別
public class Download {
public static void main(String[] args) {
String url = "https://example.com/largefile.zip";
new Thread(new Downloader(url)).start();
new Thread(new Downloader(url)).start(); // 模擬多線程
}
}
Downloader 實作 Runnable
class Downloader implements Runnable {
private static final String SAVE_DIR = "E:\\download";
private final String url;
private final String fileName;
static { new File(SAVE_DIR).mkdirs(); }
public Downloader(String url) {
this.url = url;
this.fileName = url.substring(url.lastIndexOf('/') + 1);
}
@Override public void run() {
try { download(); System.out.println(fileName + " 下載完成"); }
catch (Exception e) { e.printStackTrace(); }
}
private void download() throws Exception {
RandomAccessFile raf = new RandomAccessFile(SAVE_DIR + File.separator + fileName, "rw");
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(url);
long localSize = getLocalSize();
long remoteSize = getRemoteSize(client, get);
// 續傳條件
if (localSize != -1 && remoteSize != -1 && localSize < remoteSize) {
raf.seek(localSize);
get.addHeader("Range", "bytes=" + localSize + "-" + (remoteSize - 1));
} else if (localSize >= remoteSize) {
raf.close(); return; // 已完成
}
HttpResponse resp = client.execute(get);
if (resp.getStatusLine().getStatusCode() / 100 == 2) {
// 進度顯示執行緒
new Thread(() -> {
try {
while (raf.length() < remoteSize) {
System.out.printf("%s 已下載: %.2f%%%n", fileName,
raf.length() * 100.0 / remoteSize);
Thread.sleep(5000);
}
} catch (Exception ignored) {}
}).start();
// 寫入資料
try (InputStream in = resp.getEntity().getContent()) {
byte[] buf = new byte[512];
int len;
while ((len = in.read(buf)) != -1) raf.write(buf, 0, len);
}
}
raf.close();
client.getConnectionManager().shutdown();
}
private long getLocalSize() {
File f = new File(SAVE_DIR, fileName);
return f.exists() ? f.length() : -1;
}
private long getRemoteSize(HttpClient client, HttpGet get) throws Exception {
HttpResponse resp = client.execute(get);
int code = resp.getStatusLine().getStatusCode();
if (code / 100 == 2) return resp.getEntity().getContentLength();
return -1;
}
}
關鍵技術點解析
| 技術 | 說明 |
| Range 標頭 | Range: bytes=START-END 告知伺服器只傳輸指定區間,206 Partial Content 回應。 |
| RandomAccessFile | seek(pos) 移動檔案指標,配合 rw 模式實現隨機讀寫。 |
| 多線程進度 | 獨立執行緒輪詢 raf.length() 計算百分比,避免阻塞下載主流程。 |
| HttpClient 4.x | 範例使用 DefaultHttpClient(已棄用),建議升級至 HttpClient 5 或 OkHttp。 |
生產環境強化建議
- 分段下載:將檔案切分多段,每段由獨立線程下載,最後合併,大幅提速。
- 校驗完整性:下載完成後驗證 MD5/SHA-256。
- 錯誤重試:網路波動時指數退避重試。
- 限速/暫停/恢復:提供控制介面。
- 圖形介面:Swing/JavaFX 顯示進度條、速度、剩餘時間。
依賴 (Maven)
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>