WordPress自动本地化文章中的外联图片

在您的WordPress主题的functions.php文件中添加以下代码,以启用远程图片下载并保存到本地的功能:

/**
 * 保存文章时,自动将外部图片下载到媒体库并替换链接。
 * 跳过 v.fenxiangzu.com 域名,其他外部图片均会下载。
 */
add_action('save_post', 'custom_download_external_images', 10, 3);

function custom_download_external_images($post_id, $post, $update) {
    // 避免自动保存、修订版本等
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
    if (wp_is_post_revision($post_id)) return;
    if (wp_is_post_autosave($post_id)) return;
    if (!current_user_can('edit_post', $post_id)) return;

    $content = $post->post_content;
    if (empty($content)) return;

    // 确保必要的 WordPress 文件处理函数可用
    if (!function_exists('download_url')) {
        require_once ABSPATH . 'wp-admin/includes/file.php';
    }
    if (!function_exists('media_handle_sideload')) {
        require_once ABSPATH . 'wp-admin/includes/media.php';
    }
    if (!function_exists('wp_read_image_metadata')) {
        require_once ABSPATH . 'wp-admin/includes/image.php';
    }

    // 使用 DOMDocument 安全解析 HTML
    $dom = new DOMDocument();
    libxml_use_internal_errors(true);
    // 确保 UTF-8 内容正确处理
    $dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'));
    libxml_clear_errors();

    $images = $dom->getElementsByTagName('img');
    $changed = false;
    $site_host = parse_url(home_url(), PHP_URL_HOST);

    foreach ($images as $img) {
        $src = $img->getAttribute('src');
        if (empty($src)) continue;

        // 解析 URL 各部分
        $url_parts = parse_url($src);
        if (!isset($url_parts['host'])) continue; // 无 host,可能是 data URI 等,跳过

        $host = $url_parts['host'];

        // 跳过本站图片
        if ($host === $site_host) continue;

        // 黑名单域名:不进行下载
        $skip_hosts = ['v.fenxiangzu.com'];
        if (in_array($host, $skip_hosts)) {
            error_log('跳过黑名单域名图片: ' . $src);
            continue;
        }

        // 仅允许 http 和 https 协议
        $scheme = isset($url_parts['scheme']) ? strtolower($url_parts['scheme']) : '';
        if (!in_array($scheme, ['http', 'https'])) {
            error_log('不允许的协议: ' . $src);
            continue;
        }

        // 防止 SSRF:解析域名对应 IP,禁止内网地址
        $ip = gethostbyname($host);
        if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
            error_log('禁止内网/保留 IP 地址: ' . $host . ' (' . $ip . ')');
            continue;
        }

        // 下载图片(15秒超时)
        $tmp_file = download_url($src, 15);
        if (is_wp_error($tmp_file)) {
            error_log('下载失败: ' . $src . ' 错误: ' . $tmp_file->get_error_message());
            continue;
        }

        // 从 URL 路径中提取原始文件名,并去除查询参数
        $path = isset($url_parts['path']) ? $url_parts['path'] : '';
        $filename = basename($path);
        // 如果文件名无扩展名,尝试根据 MIME 补充
        if (!preg_match('/\.[a-zA-Z0-9]+$/', $filename)) {
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            $real_mime = finfo_file($finfo, $tmp_file);
            finfo_close($finfo);
            $ext = '';
            if ($real_mime) {
                $ext = explode('/', $real_mime)[1] ?? '';
            }
            if ($ext) {
                $filename .= '.' . $ext;
            } else {
                // 无法判断,跳过该文件
                @unlink($tmp_file);
                error_log('无法识别文件类型: ' . $src);
                continue;
            }
        }

        // 清理文件名
        $filename = sanitize_file_name($filename);

        // 准备 sideload 参数
        $file_array = [
            'name'     => $filename,
            'tmp_name' => $tmp_file
        ];

        // 导入媒体库,设置替代文本为原图片 alt 属性
        $attachment_id = media_handle_sideload($file_array, $post_id, null, [
            'post_title' => $img->getAttribute('alt') ?: ''
        ]);

        if (is_wp_error($attachment_id)) {
            @unlink($tmp_file);
            error_log('媒体库导入失败: ' . $attachment_id->get_error_message());
            continue;
        }

        // 获取新图片 URL
        $new_url = wp_get_attachment_url($attachment_id);
        if ($new_url) {
            $img->setAttribute('src', $new_url);
            $changed = true;
        }
    }

    // 如果内容有改动,则更新文章(移除钩子避免死循环)
    if ($changed) {
        remove_action('save_post', 'custom_download_external_images');
        wp_update_post([
            'ID'           => $post_id,
            'post_content' => $dom->saveHTML()
        ]);
        add_action('save_post', 'custom_download_external_images', 10, 3);
    }
}

 

上一篇 阿甘正传
下一篇 CentOS 7.x 默认源修改的教程方法