首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 依 URL 下載圖片工具類:單檔/批次、支援 Proxy、自動建目錄

     編輯:Java 綜合教學

Java 依 URL 下載圖片工具類:單檔/批次、支援 Proxy、自動建目錄

開發中常需從遠端 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 rows
for (Map<String, Object> row : rows) {
    String url = (String) row.get("image_url");
    String hotelId = String.valueOf(row.get("hotel_id"));
    String fileName = url.substring(url.lastIndexOf('/') + 1);
    String saveDir = "/data/hotel_images/" + hotelId;
    tasks.add(new ImageDownloader.ImageTask(url, fileName, saveDir));
}
ImageDownloader.downloadBatch(tasks);

進階擴充建議

  1. 並行下載:用 ExecutorService + CompletableFuture 將 downloadBatch 改為並行,控制併發數 (如 10 條線程) 避免對方伺服器封 IP。
  2. 斷點續傳:檢查本地檔案大小,發送 Range: bytes=xxx- 請求頭,支援大檔案恢復。
  3. 內容驗證:下載完成後檢查 Content-Type、Magic Number (如 JPEG: FF D8 FF) 防止存到錯誤/空檔。
  4. 重試機制:引入 Resilience4j 或簡單 for (int i=0; i<3; i++) 重試邏輯,指數退避。
  5. 非同步非阻塞:高吞吐場景可升級至 Netty / OkHttp 非阻塞客戶端。

常見坑點提醒

  • URL 中文/特殊字元:需 URLEncoder.encode(path, "UTF-8") 處理路徑段,或使用 URI 類別構建。
  • HTTPS 憑證問題:測試環境自簽憑證需配置 TrustManager 信任所有憑證 (生產嚴禁)。
  • 大檔案 OOM:嚴禁一次性 readAllBytes(),必須分塊流式寫入 (本工具類已採用 8KB 緩衝區)。
  • 檔名衝突:建議加入 UUID/時間戳/雜湊值避免覆蓋。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved