許多網站對圖片資源啟用「防盜鏈」:僅允許自家域名的 Referer 請求下載圖片。若直接用 new URL(imgUrl).openStream() 抓取,伺服器會返回 403 Forbidden。解決之道是在 HTTP 請求中偽裝 Referer 與 Host 標頭,模擬瀏覽器行為。
package cn.searchphoto.util;
import java.io.*;
import java.net.*;
import java.util.zip.GZIPInputStream;
public class ImageDownloader {
/**
* 下載遠端圖片至本地檔案
* @param imgUrl 圖片完整 URL
* @param destFile 目標檔案物件
* @return 成功返回檔案,失敗返回 null
*/
public static File download(String imgUrl, File destFile) {
try {
URL url = new URL(imgUrl);
URLConnection conn = url.openConnection();
// 1. 解析 Host:從 URL 第 7 字元(略過 https://)到第一個 '/' 為止
int hostEnd = imgUrl.indexOf('/', 7);
String host = (hostEnd == -1) ? imgUrl.substring(7) : imgUrl.substring(7, hostEnd);
conn.setRequestProperty("Host", host);
// 2. 關鍵:設定 Referer 為圖片頁面 URL(通常即圖片本身或其所在頁面)
conn.setRequestProperty("Referer", imgUrl);
// 3. 模擬瀏覽器 User-Agent,部分站點會檢查
conn.setRequestProperty("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
// 4. 處理 GZIP 壓縮回應
InputStream is = conn.getInputStream();
String encoding = conn.getContentEncoding();
if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
is = new GZIPInputStream(is);
}
// 5. 流式寫入檔案
try (OutputStream os = new FileOutputStream(destFile)) {
byte[] buffer = new byte[4096];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
} finally {
try { is.close(); } catch (Exception ignore) {}
}
return destFile;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
HttpClient 或 OkHttp 等成熟庫替代原生 URLConnection。public static void main(String[] args) {
File out = new File("D:/temp/test.jpg");
boolean ok = ImageDownloader.download(
"https://example.com/images/photo.jpg", out) != null;
System.out.println("下載" + (ok ? "成功" : "失敗") + ",檔案大小:" + out.length() + " bytes");
}