
網站搬家、或以前把圖片放在免費圖床,文章裡的圖片網址常常還指向別的地方:
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 );
}
}程式在做什麼
- 只在文章「已發佈」時執行,修訂版本、自動儲存、草稿都跳過。草稿還在修改,不急著搬圖。
- 找出內文裡所有
<img>的網址,只處理清單內的網域,其他網域的圖片原封不動。 - 同一張圖只下載一次。每張下載過的圖片,都記下它原本的網址(
_zw_source_url)。下次遇到同一個網址,直接用媒體庫裡那張。 - 用 WordPress 內建的
media_sideload_image()下載,它會:把檔案存進上傳資料夾、建立媒體庫項目、產生各種尺寸的縮圖。 - 下載失敗(圖片已經不存在、對方網站連不上)就跳過,保留原本的網址,文章不會因此壞掉。
- 全部換好後更新文章內容。更新文章會再觸發一次「儲存文章」,所以更新前先把這段程式暫時拿掉,更新完再掛回去,避免無窮迴圈。
實際測試的結果
我們在電腦上另外開了一個小網站,假裝是「舊網站」,放了幾張圖片,然後發佈一篇文章,內文有:
- 舊網站的
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 年經驗的基隆網頁設計工作室,舊網站改版、搬家時會把圖片和連結一起整理好,台北與全台客戶也歡迎來聊聊。







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