0%

正威網路行銷工作室,專注於基隆、台北與全台客製化網頁設計,提供專業 RWD 響應式、SEO 搜尋引擎優化、品牌形象設計與網站維護託管服務。

搜尋文章
聯絡資訊
地址基隆市仁二路 220 號 5 樓
追蹤我們

WordPress 把外站圖片自動下載進自己的媒體庫

WordPress 把外站圖片自動下載進自己的媒體庫 封面

網站搬家、或以前把圖片放在免費圖床,文章裡的圖片網址常常還指向別的地方:

HTML
<img src="https://old.example.com/uploads/photo.jpg">

只要舊網站關掉、圖床停止服務,這些文章就全部變成破圖。這篇的做法是:發佈或更新文章時,自動把指定網域的圖片下載到自己的媒體庫,網址換成自己網站的。

先講清楚:只搬你有權使用的圖片

這段程式碼設計成只處理你指定的網域,例如你自己的舊網站、你自己的圖床帳號。

別人網站上的圖片,就算技術上下載得到,也不代表你可以用。下載到自己網站,等於把別人的作品放在自己的主機上公開,比直接連結的侵權問題更明確。圖片授權的觀念可以看網站圖片和字型要有授權。

程式碼

放在子佈景主題的 functions.php,或用 Code Snippets 這類外掛新增。把 old.example.com 換成你自己舊網站或圖床的網域,可以放好幾個:

PHP
// 發佈或更新文章時,把指定網域的外站圖片下載到媒體庫,並換成自己網站的網址
add_action( 'save_post_post', 'zw_import_external_images', 20, 2 );

function zw_import_external_images( $post_id, $post ) {
	if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) || 'publish' !== $post->post_status ) {
		return;
	}
	// 只搬這些網域的圖片:你自己的舊網站、舊圖床
	$allowed_hosts = array( 'old.example.com' );

	if ( ! preg_match_all( '/<img[^>]+src=["\']([^"\']+)["\']/i', $post->post_content, $matches ) ) {
		return;
	}
	require_once ABSPATH . 'wp-admin/includes/media.php';
	require_once ABSPATH . 'wp-admin/includes/file.php';
	require_once ABSPATH . 'wp-admin/includes/image.php';

	$content = $post->post_content;
	foreach ( array_unique( $matches[1] ) as $url ) {
		$host = wp_parse_url( $url, PHP_URL_HOST );
		if ( ! $host || ! in_array( $host, $allowed_hosts, true ) ) {
			continue;
		}
		// 同一張圖下載過就直接用,不重複下載
		$existing = get_posts( array(
			'post_type'   => 'attachment',
			'post_status' => 'inherit',
			'meta_key'    => '_zw_source_url',
			'meta_value'  => $url,
			'fields'      => 'ids',
			'numberposts' => 1,
		) );
		if ( $existing ) {
			$attachment_id = $existing[0];
		} else {
			$attachment_id = media_sideload_image( $url, $post_id, null, 'id' );
			if ( is_wp_error( $attachment_id ) ) {
				continue; // 下載失敗就保留原本的網址
			}
			update_post_meta( $attachment_id, '_zw_source_url', $url );
		}
		$content = str_replace( $url, wp_get_attachment_url( $attachment_id ), $content );
	}

	if ( $content !== $post->post_content ) {
		// 先拿掉自己再更新,不然更新文章又會觸發這段,變成無窮迴圈
		remove_action( 'save_post_post', 'zw_import_external_images', 20 );
		wp_update_post( array( 'ID' => $post_id, 'post_content' => $content ) );
		add_action( 'save_post_post', 'zw_import_external_images', 20, 2 );
	}
}

程式在做什麼

  1. 只在文章「已發佈」時執行,修訂版本、自動儲存、草稿都跳過。草稿還在修改,不急著搬圖。
  2. 找出內文裡所有 <img> 的網址,只處理清單內的網域,其他網域的圖片原封不動。
  3. 同一張圖只下載一次。每張下載過的圖片,都記下它原本的網址(_zw_source_url)。下次遇到同一個網址,直接用媒體庫裡那張。
  4. 用 WordPress 內建的 media_sideload_image() 下載,它會:把檔案存進上傳資料夾、建立媒體庫項目、產生各種尺寸的縮圖。
  5. 下載失敗(圖片已經不存在、對方網站連不上)就跳過,保留原本的網址,文章不會因此壞掉。
  6. 全部換好後更新文章內容。更新文章會再觸發一次「儲存文章」,所以更新前先把這段程式暫時拿掉,更新完再掛回去,避免無窮迴圈。

實際測試的結果

我們在電腦上另外開了一個小網站,假裝是「舊網站」,放了幾張圖片,然後發佈一篇文章,內文有:

  • 舊網站的 hero.jpg,出現兩次:只下載了一次,兩個地方都換成自己網站的網址。
  • 舊網站的 hero-mobile.webp:WebP 格式也正常下載、替換。
  • 舊網站上不存在的 no-such.jpg:下載失敗,網址保留原樣。
  • 不在清單內的網域的圖片:完全沒動。

媒體庫多了 2 張圖,就是那兩張實際存在的圖片。

區塊編輯器要重新整理一次

在區塊編輯器發佈一篇有外站圖片的文章,資料庫裡的網址已經換好了,但編輯器畫面上還是舊網址,因為編輯器不知道伺服器偷偷改了內容。

這時如果繼續編輯、再按一次「更新」,編輯器會把舊網址再送回去。測試結果是:程式又把它換成媒體庫那張,沒有重複下載,網址一樣正確。不過最好的習慣還是:發佈後重新整理一次編輯器,畫面上就會是新的網址了。

限制和注意事項

  • 只處理 <img> 標籤。CSS 背景圖、頁面編輯器(例如 Elementor)存在別的地方的圖片,不會處理。
  • 網址要是圖片檔名。media_sideload_image() 預設只接受 .jpg、.jpeg、.png、.gif、.webp 結尾的網址(AVIF 也不行);圖床的網址如果沒有副檔名(例如 ?id=123),會下載失敗、保留原樣。
  • WordPress 不會去下載內部網路的位址。192.168. 開頭、localhost 這類網址,或不常見的連接埠,WordPress 基於安全會直接拒絕,這是正常的保護。
  • 舊文章不會自己變。這段程式碼只在發佈、更新文章時執行。舊文章很多的話,可以用「批次編輯」把一批文章一起按更新,每批不要太多,下載圖片要時間,一次太多可能逾時。
  • 先備份。內容會被改寫,執行前確定有網站和資料庫的備份。

延伸閱讀

網站搬家最怕的就是圖片、連結一個一個壞掉。正威網路行銷是 25 年經驗的基隆網頁設計工作室,舊網站改版、搬家時會把圖片和連結一起整理好,台北與全台客戶也歡迎來聊聊。

分享這篇文章

返回文章列表更多「Wordpress代碼教學」文章

發表留言

還沒有人留言,歡迎第一個分享你的想法或問題。留言經審核後才會顯示,Email 不會公開。

讓我們一起打造
您的專屬網站!