Java 提供 java.net.URL 與 URLConnection 實現統一的資源存取介面,支援 HTTP、HTTPS、FTP 等協議。本文提供生產級下載工具類,涵蓋斷點續傳、進度回呼、連線池、SSL 忽略憑證等進階功能。
public boolean download(String urlString, String destPath) {
try (InputStream in = new URL(urlString).openStream();
FileOutputStream out = new FileOutputStream(destPath)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
return true;
} catch (IOException e) {
log.error("Download failed: {}", urlString, e);
return false;
}
}
public class HttpDownloader {
private static final int BUFFER_SIZE = 8192;
private static final int CONNECT_TIMEOUT = 10_000;
private static final int READ_TIMEOUT = 30_000;
public void download(String urlString, File destFile, ProgressListener listener)
throws IOException {
long existingLength = destFile.exists() ? destFile.length() : 0;
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 設定連線參數
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(READ_TIMEOUT);
conn.setRequestProperty("User-Agent", "Java Downloader 1.0");
// 斷點續傳:Range 標頭
if (existingLength > 0) {
conn.setRequestProperty("Range", "bytes=" + existingLength + "-");
}
int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_PARTIAL || responseCode == HttpURLConnection.HTTP_OK) {
long contentLength = conn.getContentLengthLong();
long totalLength = existingLength + contentLength;
try (InputStream in = conn.getInputStream();
RandomAccessFile raf = new RandomAccessFile(destFile, "rw")) {
raf.seek(existingLength);
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
long downloaded = existingLength;
while ((bytesRead = in.read(buffer)) != -1) {
raf.write(buffer, 0, bytesRead);
downloaded += bytesRead;
if (listener != null) {
listener.onProgress(downloaded, totalLength);
}
}
}
} else if (responseCode == HttpURLConnection.HTTP_RANGE_NOT_SATISFIABLE) {
// 檔案已完整
if (listener != null) listener.onProgress(existingLength, existingLength);
} else {
throw new IOException("Server returned: " + responseCode);
}
}
@FunctionalInterface
public interface ProgressListener {
void onProgress(long downloaded, long total);
}
}
// 必須在建立連線前設置
TrustManager[] trustAll = new TrustManager[] {
new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}
};
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAll, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
// URL 格式:ftp://user:pass@host:port/path/to/file
URL url = new URL("ftp://user:password@ftp.example.com/pub/file.zip");
URLConnection conn = url.openConnection();
// FTP 不支援 Range 斷點續傳,需使用 Apache Commons Net FTPClient
| 函式庫 | Maven 坐標 | 特色 |
|---|---|---|
| Apache HttpClient | org.apache.httpcomponents:httpclient:4.5.x | 連線池、重試、攔截器、非同步、完整 HTTP/1.1 |
| OkHttp | com.squareup.okhttp3:okhttp:4.x | HTTP/2、連線池、透明 GZIP、現代 API |
| Spring RestTemplate/WebClient | org.springframework:spring-web | Spring 生態整合、宣告式重試、反應式 |
| Apache Commons Net | commons-net:commons-net:3.x | FTP/FTPS/SFTP 完整支援 |