開發中常需從遠端 URL 抓取圖片落地儲存。本文提供一個生產就緒的 ImageDownloader 工具類,支援連線/讀取逾時、Proxy 設定、自動建立多層目錄、大檔案分塊寫入,並示範批次下載呼叫方式。
import java.io.*;
import java.net.*;
public class ImageDownloader {
private static final int DEFAULT_TIMEOUT_MS = 5000;
private static final int BUFFER_SIZE = 8192; // 8KB
/**
* 下載單張圖片
* @param urlString 圖片完整 URL (例: https://cdn.example.com/img/photo.jpg)
* @param fileName 儲存檔名 (含副檔名),若為 null 則從 URL 解析
* @param saveDir 儲存目錄絕對路徑 (例: /data/images/hotel_1001)
* @throws IOException 網路/IO 異常
*/
public static void download(String urlString, String fileName, String saveDir) throws IOException {
URL url = new URL(urlString);
URLConnection conn = url.openConnection();
// 可選:設定 Proxy (若環境需經代理)
// System.setProperty("http.proxyHost", "proxy.example.com");
// System.setProperty("http.proxyPort", "3128");
conn.setConnectTimeout(DEFAULT_TIMEOUT_MS);
conn.setReadTimeout(DEFAULT_TIMEOUT_MS);
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; ImageDownloader/1.0)");
// 解析檔名
if (fileName == null || fileName.isEmpty()) {
String path = url.getPath();
fileName = path.substring(path.lastIndexOf('/') + 1);
if (fileName.isEmpty()) fileName = "image_" + System.currentTimeMillis() + ".jpg";
}
// 建立目錄
File dir = new File(saveDir);
if (!dir.exists() && !dir.mkdirs()) {
throw new IOException("無法建立目錄: " + saveDir);
}
File outFile = new File(dir, fileName);
try (InputStream in = conn.getInputStream();
OutputStream out = new BufferedOutputStream(new FileOutputStream(outFile), BUFFER_SIZE)) {
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
out.flush();
}
}
/**
* 批次下載 (同步循環,簡單場景適用;大量建議用 CompletableFuture/線程池並行)
*/
public static void downloadBatch(List<ImageTask> tasks) {
for (ImageTask task : tasks) {
try {
download(task.url, task.fileName, task.saveDir);
System.out.println("[OK] " + task.url + " -> " + task.saveDir + "/" + task.fileName);
} catch (IOException e) {
System.err.println("[FAIL] " + task.url + " : " + e.getMessage());
}
}
}
// 任務資料類
public static class ImageTask {
public final String url;
public final String fileName;
public final String saveDir;
public ImageTask(String url, String fileName, String saveDir) {
this.url = url; this.fileName = fileName; this.saveDir = saveDir;
}
}
}
public static void main(String[] args) {
try {
ImageDownloader.download(
"https://example.com/hotels/10001/cover.jpg",
"cover.jpg",
"/var/www/images/hotel_10001"
);
System.out.println("下載完成");
} catch (IOException e) {
e.printStackTrace();
}
}
List<ImageDownloader.ImageTask> tasks = new ArrayList<>(); // 假設從 DB 查詢得到 List
ExecutorService + CompletableFuture 將 downloadBatch 改為並行,控制併發數 (如 10 條線程) 避免對方伺服器封 IP。Range: bytes=xxx- 請求頭,支援大檔案恢復。Content-Type、Magic Number (如 JPEG: FF D8 FF) 防止存到錯誤/空檔。Resilience4j 或簡單 for (int i=0; i<3; i++) 重試邏輯,指數退避。URLEncoder.encode(path, "UTF-8") 處理路徑段,或使用 URI 類別構建。TrustManager 信任所有憑證 (生產嚴禁)。readAllBytes(),必須分塊流式寫入 (本工具類已採用 8KB 緩衝區)。