在 Web 開發中,經常需要從外部來源下載圖片並儲存到伺服器端,例如建立圖片代理、快取外部圖片資源、或是批次匯入商品圖片等情境。PHP 提供了多種方式達成此需求,本文將介紹一個實用的函式 GrabImage(),並說明其運作原理與注意事項。
<?php
/**
* 取得遠端圖片並儲存至本地
*
* @param string $url 遠端圖片完整 URL,不可為空
* @param string $filename 選填,本地儲存檔名;若為空則依日期時間自動產生
* @return string|bool 成功回傳檔名,失敗回傳 false
*/
function GrabImage($url, $filename = '') {
// 參數檢核
if (empty($url)) {
return false;
}
// 自動產生檔名(若未指定)
if (empty($filename)) {
$ext = strrchr($url, '.');
// 僅支援 GIF 與 JPG 格式
if (!in_array(strtolower($ext), ['.gif', '.jpg', '.jpeg'])) {
return false;
}
$filename = date('dMYHis') . $ext;
}
// 使用輸出緩衝捕獲圖片二進位資料
ob_start();
readfile($url);
$img = ob_get_contents();
ob_end_clean();
$size = strlen($img);
if ($size === 0) {
return false; // 圖片內容為空
}
// 寫入檔案(以附加模式開啟,實務上建議改用 'wb' 覆寫模式)
$fp = @fopen($filename, 'wb');
if (!$fp) {
return false; // 無寫入權限或路徑錯誤
}
fwrite($fp, $img);
fclose($fp);
return $filename;
}
// 使用範例
$img = GrabImage('https://news.bbc.co.uk/images/_1978837_detector_ap100.jpg', '');
if ($img) {
echo '<pre><img src="' . $img . '"></pre>';
} else {
echo 'false';
}
?>
透過 ob_start()、ob_get_contents()、ob_end_clean() 組合,可將 readfile() 直接輸出的二進位資料捕獲到變數中,避免直接輸出到瀏覽器。
原始碼使用 'a'(附加模式),實務上建議改為 'wb'(二進位覆寫模式),確保每次寫入都是完整、獨立的檔案,避免殘留舊資料。
僅允許 .gif、.jpg、.jpeg 副檔名,實際專案建議搭配 getimagesize() 或 finfo_file() 驗證真實 MIME 類型,防止惡意檔案上傳。
try-catch 或詳細錯誤碼回傳,方便除錯。openssl 擴充,readfile() 才能正常讀取 HTTPS 資源。stream_context_create() 設定連線與讀取逾時,避免卡住程序。www-data 或 nginx 使用者)。⚠️ 切勿直接信任使用者輸入的 URL,必須實施白名單機制、驗證網域、限制內部 IP 存取(防止 SSRF 攻擊),並掃描下載檔案是否包含惡意程式碼。
GrabImage() 提供了一個簡潔的基礎範例,展示 PHP 处理远程资源下载与本地存储的核心流程。在生产环境中,建议在此基础上加强异常处理、格式验证、安全防护与日志记录,构建健壮的图片代理或同步服务。