我们的 WordPress 网站在使用了图床,或者复制粘贴了别人网站内容的情况下,文章中会出现一些外部图片(地址)。如果使用的图床停止服务(尤其是白嫖的免费图床),或者别人的网站挂了,就会导致我们网站文章中的外链图片也随之失效,无法显示。
此时,如果提前备份了图片还好,重新上传或者批量替换地址什么的就可以了,无非就是麻烦一些。但如果图片没有备份,那就糟糕了,重新给所有文章配图可不是一件容易的事。
那么, 如何避免这个问题呢?最保险的做法自然是将文章中的外部图片,下载并保存到 WordPress 媒体库,并将文章中的图片链接替换为本地服务器的图片链接。实现也很简单,无需安装插件,直接将下方代码加入到主题 functions.php 文件中,之后更新发布文章就会看到效果了。
function ecp_save_post($post_id, $post) { global $wpdb; if ($post->post_status == 'publish') { $p = '/<img.*[\s]src=[\"|\'](.*)[\"|\'].*>/iU'; $num = preg_match_all($p, $post->post_content, $matches); if ($num) { $wp_upload_dir = wp_upload_dir(); set_time_limit(0); $ch = curl_init(); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_MAXREDIRS, 20); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); $ecp_options = $_SERVER['HTTP_HOST']; foreach ($matches[1] as $src) { if (isset($src) && strpos($src, $ecp_options) === false) { $file_info = wp_check_filetype(basename($src), null); if ($file_info['ext'] == false) { date_default_timezone_set('PRC'); $file_name = date('YmdHis-') . dechex(mt_rand(100000, 999999)) . '.tmp'; } else { $file_name = dechex(mt_rand(100000, 999999)) . '-' . basename($src); } curl_setopt($ch, CURLOPT_URL, $src); $file_path = $wp_upload_dir['path'] . '/' . $file_name; $img = fopen($file_path, 'wb'); curl_setopt($ch, CURLOPT_FILE, $img); $img_data = curl_exec($ch); fclose($img); if (file_exists($file_path) && filesize($file_path) > 0) { $t = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); $arr = explode('/', $t); if (pathinfo($file_path, PATHINFO_EXTENSION) == 'tmp') { $file_path = ecp_handle_ext($file_path, $arr[1], $wp_upload_dir['path'], $file_name, 'tmp'); } $post->post_content = str_replace($src, $wp_upload_dir['url'] . '/' . basename($file_path), $post->post_content); $attachment = ecp_get_attachment_post(basename($file_path), $wp_upload_dir['url'] . '/' . basename($file_path)); $attach_id = wp_insert_attachment($attachment, ltrim($wp_upload_dir['subdir'] . '/' . basename($file_path), '/'), 0); $attach_data = wp_generate_attachment_metadata($attach_id, $file_path); $ss = wp_update_attachment_metadata($attach_id, $attach_data); } } } curl_close($ch); $wpdb->update($wpdb->posts, array('post_content' => $post->post_content), array('ID' => $post->ID)); } } } function ecp_handle_ext($file, $type, $file_dir, $file_name, $ext) { if ($ext === 'tmp') { if (rename($file, str_replace('tmp', $type, $file))) { return $file_dir . '/' . str_replace('tmp', $type, $file_name); } } return $file; } function ecp_get_attachment_post($filename, $url) { $file_info = wp_check_filetype($filename, null); return array( 'guid' => $url, 'post_type' => 'attachement', 'post_mime_type' => $file_info['type'], 'post_title' => preg_replace('/\.[^.]+$/', '', $filename), 'post_content' => '', 'post_status' => 'inherit' ); } add_action('save_post', 'ecp_save_post', 120, 2);
不过,也有这种情况和需求:网站使用了国外服务器,但把图片放在国内服务器上(如利用国内大厂的某些服务当作免费图床)以提升加载速度。因此希望可以暂时使用国内的图片外链,只是先将外链图片提前备份到媒体库。当某天万一外链图片挂了,再替换成内链图片。
如何实现呢?下面提供两段代码,任选其一即可。代码 1 是在上述代码基础上修改而来。
-
保持原外链图片文件名和格式,不再自动重命名上传的图片 -
仅上传外链图片至媒体库,不再自动替换文章中的图片链接地址
代码 1
function ecp_save_post($post_id, $post) { global $wpdb; if ($post->post_status == 'publish') { $p = '/<img.*[\s]src=[\"|\'](.*)[\"|\'].*>/iU'; $num = preg_match_all($p, $post->post_content, $matches); if ($num) { $wp_upload_dir = wp_upload_dir(); set_time_limit(0); $ch = curl_init(); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_MAXREDIRS, 20); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); $ecp_options = $_SERVER['HTTP_HOST']; foreach ($matches[1] as $src) { if (isset($src) && strpos($src, $ecp_options) === false) { $attachment_id = attachment_url_to_postid($src); if ($attachment_id) { continue; } $file_name = basename($src); $file_path = $wp_upload_dir['path'] . '/' . $file_name; if (file_exists($file_path)) { continue; } curl_setopt($ch, CURLOPT_URL, $src); $img = fopen($file_path, 'wb'); curl_setopt($ch, CURLOPT_FILE, $img); $img_data = curl_exec($ch); fclose($img); if (file_exists($file_path) && filesize($file_path) > 0) { $attachment = ecp_get_attachment_post($file_name, $wp_upload_dir['url'] . '/' . $file_name); $attach_id = wp_insert_attachment($attachment, $file_path, 0); $attach_data = wp_generate_attachment_metadata($attach_id, $file_path); wp_update_attachment_metadata($attach_id, $attach_data); } } } curl_close($ch); } } } function ecp_get_attachment_post($filename, $url) { $file_info = wp_check_filetype($filename, null); return array( 'guid' => $url, 'post_type' => 'attachment', 'post_mime_type' => $file_info['type'], 'post_title' => preg_replace('/\.[^.]+$/', '', $filename), 'post_content' => '', 'post_status' => 'inherit' ); } add_action('save_post', 'ecp_save_post', 120, 2);
代码 2
function backup_external_images_to_media_library( $post_id ) { $post = get_post( $post_id ); $content = $post->post_content; // 使用正则匹配图片 preg_match_all( '/<img[^>]+src="([^">]+)"/i', $content, $matches ); if ( isset( $matches[1] ) && !empty( $matches[1] ) ) { foreach ( $matches[1] as $image_url ) { // 跳过本站内图片 if ( strpos( $image_url, home_url() ) === false ) { $attachment_id = attachment_url_to_postid( $image_url ); if ( !$attachment_id ) { // 上传图片到媒体库 $upload = upload_image_to_media_library_without_replacement( $image_url ); if ( $upload ) { // 可根据需要处理成功上传的逻辑 } } } } } } function upload_image_to_media_library_without_replacement( $image_url ) { // 下载图片 $image_data = file_get_contents( $image_url ); if ( !$image_data ) { return false; } // 生成唯一文件名 $filename = basename( parse_url( $image_url, PHP_URL_PATH ) ); $upload_dir = wp_upload_dir(); $upload_path = $upload_dir['path'] . '/' . $filename; // 检查文件是否已经存在 if ( file_exists( $upload_path ) ) { return false; } // 保存图片到上传目录 file_put_contents( $upload_path, $image_data ); // 创建附件 $file_type = wp_check_filetype( $filename, null ); $attachment = array( 'guid' => $upload_dir['url'] . '/' . basename( $filename ), 'post_mime_type' => $file_type['type'], 'post_title' => sanitize_file_name( $filename ), 'post_content' => '', 'post_status' => 'inherit', ); // 将附件插入到媒体库 $attachment_id = wp_insert_attachment( $attachment, $upload_path ); // 生成并更新元数据 require_once( ABSPATH . 'wp-admin/includes/image.php' ); $attachment_data = wp_generate_attachment_metadata( $attachment_id, $upload_path ); wp_update_attachment_metadata( $attachment_id, $attachment_data ); return array( 'id' => $attachment_id, 'url' => wp_get_attachment_url( $attachment_id ), ); } // 注册文章保存钩子 add_action( 'save_post', 'backup_external_images_to_media_library' );
哪段代码更好?
代码 1 的优势在于它使用了 cURL
,可以更好地处理复杂的网络请求,如大图片下载、重定向等场景。如果需要处理更多不确定性因素(如外部服务器响应时间较长、复杂的重定向情况),第一段代码可能更适合,但也需要更复杂的维护和优化。
代码 2 更加简洁、易读、模块化,且错误处理更清晰,适合大多数常见场景。它的实现更符合现代编程习惯,代码维护性较好。如果没有特殊的超时或重定向处理需求,它的实现方式较优。
因此,如果目标是简单的下载和备份外部图片,推荐使用 代码 2,因为它更加简洁,且易于扩展和维护。如果需要处理更多复杂的网络请求,如跨站点的大文件下载或重定向,代码 1 的 cURL
方式可能更适合。
最后,如果仅仅先备份图片到媒体库,小白建议不要在内链图片地址中包含年份和月份(日期),以方便以后替换链接。只需要在 WordPress 网站后台 - 设置 - 媒体 - 文件上传,取消勾选”以年—月目录形式组织上传内容“就可以了。
代码 3
在代码 2 基础上进一步优化
function backup_external_images_to_media_library( $post_id ) { // 获取文章内容 $post = get_post( $post_id ); if ( empty( $post ) || wp_is_post_revision( $post_id ) ) { return; } $content = $post->post_content; // 提前检查 <img> 标签以减少不必要的正则匹配 if ( strpos( $content, '<img' ) === false ) { return; } // 使用 DOMDocument 来解析图片 libxml_use_internal_errors( true ); // 忽略 HTML 解析中的警告 $dom = new DOMDocument(); $dom->loadHTML( $content ); $images = $dom->getElementsByTagName( 'img' ); // 缓存已经处理的 URL $processed_urls = array(); foreach ( $images as $img ) { $image_url = $img->getAttribute( 'src' ); // 跳过本站内的图片 if ( strpos( $image_url, home_url() ) !== false || in_array( $image_url, $processed_urls ) ) { continue; } // 检查缓存,避免重复下载相同的图片 $cached_attachment_id = get_transient( 'external_image_' . md5( $image_url ) ); if ( $cached_attachment_id ) { $processed_urls[] = $image_url; continue; } // 异步上传图片 wp_schedule_single_event( time(), 'process_external_image', array( $image_url ) ); } } // 异步处理外部图片下载和上传 function process_external_image( $image_url ) { if ( empty( $image_url ) ) { return; } // 下载并上传图片 $upload = upload_image_to_media_library_without_replacement( $image_url ); if ( !empty( $upload['id'] ) ) { // 设置 transient 缓存,防止重复处理 set_transient( 'external_image_' . md5( $image_url ), $upload['id'], DAY_IN_SECONDS ); } } // 上传图片的函数保持不变,只做轻微调整 function upload_image_to_media_library_without_replacement( $image_url ) { // 下载图片数据 $response = wp_remote_get( $image_url, array( 'timeout' => 15 ) ); $image_data = wp_remote_retrieve_body( $response ); if ( empty( $image_data ) ) { return false; } // 获取唯一文件名 $filename = wp_unique_filename( wp_upload_dir()['path'], basename( parse_url( $image_url, PHP_URL_PATH ) ) ); $upload_dir = wp_upload_dir(); $upload_path = $upload_dir['path'] . '/' . $filename; // 保存图片到上传目录 file_put_contents( $upload_path, $image_data ); // 创建附件 $file_type = wp_check_filetype( $filename, null ); $attachment = array( 'guid' => $upload_dir['url'] . '/' . basename( $filename ), 'post_mime_type' => $file_type['type'], 'post_title' => sanitize_file_name( $filename ), 'post_content' => '', 'post_status' => 'inherit', ); $attachment_id = wp_insert_attachment( $attachment, $upload_path ); // 生成元数据并更新 require_once( ABSPATH . 'wp-admin/includes/image.php' ); $attachment_data = wp_generate_attachment_metadata( $attachment_id, $upload_path ); wp_update_attachment_metadata( $attachment_id, $attachment_data ); return array( 'id' => $attachment_id, 'url' => wp_get_attachment_url( $attachment_id ), ); } // 注册异步任务的事件 add_action( 'process_external_image', 'process_external_image' ); // 注册文章保存钩子 add_action( 'save_post', 'backup_external_images_to_media_library' );
评论0