HttpURLConnection 發起 GET 請求,設置超時、User-Agent、Range 頭。conn.getInputStream() 讀取響應體。import java.io.*;
import java.net.*;
import java.util.*;
public class HttpQueueDownloader {
private static final int BUFFER_SIZE = 8192;
private final List<DownloadTask> tasks = new ArrayList<>();
private final boolean debug;
public HttpQueueDownloader(boolean debug) { this.debug = debug; }
public void addTask(String url, String destFile) {
tasks.add(new DownloadTask(url, destFile));
}
public void start() {
for (DownloadTask task : tasks) {
try {
download(task);
log("完成:" + task.destFile);
} catch (IOException e) {
log("失敗:" + task.url + " - " + e.getMessage());
}
}
}
private void download(DownloadTask task) throws IOException {
URL url = new URL(task.url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
setupProxy(conn);
conn.setRequestMethod("GET");
conn.setConnectTimeout(10000);
conn.setReadTimeout(30000);
conn.setRequestProperty("User-Agent", "JavaDownloader/1.0");
// 斷點續傳:若文件已存在,設置 Range
File file = new File(task.destFile);
long existing = file.length();
if (existing > 0) {
conn.setRequestProperty("Range", "bytes=" + existing + "-");
}
int code = conn.getResponseCode();
if (code == 416) { // Range Not Satisfiable -> 已下載完
return;
}
if (code >= 400) {
throw new IOException("HTTP " + code + " " + conn.getResponseMessage());
}
try (InputStream in = new BufferedInputStream(conn.getInputStream());
RandomAccessFile raf = new RandomAccessFile(file, "rw")) {
raf.seek(existing);
byte[] buf = new byte[BUFFER_SIZE];
int len;
while ((len = in.read(buf)) != -1) {
raf.write(buf, 0, len);
}
}
}
private void setupProxy(HttpURLConnection conn) {
// 可從系統屬性或配置讀取
String proxyHost = System.getProperty("proxyHost");
String proxyPort = System.getProperty("proxyPort");
if (proxyHost != null && proxyPort != null) {
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, Integer.parseInt(proxyPort)));
// 注意:HttpURLConnection 不直接支援 Proxy 參數,需用 URL.openConnection(proxy)
// 此處簡化,實際建構 URL 時傳入 Proxy
}
}
private void log(String msg) {
if (debug) System.out.println("[Downloader] " + msg);
}
static class DownloadTask {
final String url;
final String destFile;
DownloadTask(String url, String destFile) {
this.url = url;
this.destFile = destFile;
}
}
// 使用示例
public static void main(String[] args) {
HttpQueueDownloader dl = new HttpQueueDownloader(true);
dl.addTask("https://example.com/file1.zip", "data/file1.zip");
dl.addTask("https://example.com/file2.zip", "data/file2.zip");
dl.start();
}
}
new URL(url).openConnection(proxy) 而非系統屬性,線程安全。Range: bytes=start- + RandomAccessFile.seek()。Content-Length 或計算 MD5/SHA-256。ExecutorService + Semaphore 限制並發數,避免佔滿帶寬/文件句柄。ProgressListener 接口,UI 層訂閱顯示進度條。若需工業級穩健性,建議直接集成成熟庫: