WordPress 外链图像自动下载至本地教程

前言

嵌入外部链接图像,虽可节省本地空间,但也盗刷了他人服务器(或CDN)的流量,这种行为也被称为”盗链”,虽然我们在发表文章主动上传图像即可避免此类问题,但如果是一个开放式的博客(或论坛),就很难避免此类情况的发生,因此这需要可自动将外链图像保存至本地的方法。

正题

<?php
function ecp_save_post($post_id, $post) {
    global $wpdb;
    if ($post->post_status == 'publish') {
        // 匹配文章内容内的图片 URL
        $p = '/<img.*[\s]src=[\"|\'](.*)[\"|\'].*>/iU';
        $num = preg_match_all($p, $post->post_content, $matches);
        if ($num) {
            $wp_upload_dir = wp_upload_dir();
            $default_image_url = 'https://example.com/post/default-image.webp'; // 默认图像
            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'];

            // 定义 MIME 类型映射为文件扩展名
            $ext_map = array(
                'image/jpeg'    => 'jpg',
                'image/jfif'    => 'jfif',
                'image/png'     => 'png',
                'image/bmp'     => 'bmp',
                'image/gif'     => 'gif',
                'image/webp'    => 'webp',
                'image/avif'    => 'avif',
                'image/tiff'    => 'tiff',
                'image/x-icon'  => 'ico',
                'image/svg+xml' => 'svg'
            );

            foreach ($matches[1] as $src) {
                // 仅处理外部图片链接
                if (isset($src) && strpos($src, $ecp_options) === false) {
                    // 获取图片 MIME 类型
                    $file_info = @getimagesize($src);
                    $mime_type = isset($file_info['mime']) ? $file_info['mime'] : 'application/octet-stream';
                    $file_ext = isset($ext_map[$mime_type]) ? $ext_map[$mime_type] : 'unknown';
                    $original_file_name = substr(md5(basename($src)), 0, 10) . current_time('YmdHis') . '.' . $file_ext;
                    $original_file_path = $wp_upload_dir['path'] . '/' . $original_file_name;

                    // 下载图片并同时使用 jsproxy 进行加速,同时以隐藏服务器真实IP
                    curl_setopt($ch, CURLOPT_URL, "https://jsproxy.example.com/" . $src);
                    $img = fopen($original_file_path, 'wb');
                    curl_setopt($ch, CURLOPT_FILE, $img);
                    curl_exec($ch);
                    fclose($img);

                    if (!file_exists($original_file_path) || filesize($original_file_path) == 0) {
                        $post->post_content = str_replace($src, $default_image_url, $post->post_content);
                        continue;
                    }

                    // 定义转换后文件名称(WebP 格式)
                    $webp_file_name = preg_replace('/\.[^.]+$/', '', $original_file_name) . '.webp';
                    $webp_file_path = $wp_upload_dir['path'] . '/' . $webp_file_name;
                    $image = null;

                    switch ($mime_type) {
                        case 'image/jpeg':
                        case 'image/jfif':
                            if (function_exists('imagecreatefromjpeg')) {
                                $image = imagecreatefromjpeg($original_file_path);
                            }
                            break;
                        case 'image/png':
                            if (function_exists('imagecreatefrompng')) {
                                $image = imagecreatefrompng($original_file_path);
                            }
                            break;
                        case 'image/bmp':
                            if (function_exists('imagecreatefrombmp')) {
                                $image = imagecreatefrombmp($original_file_path);
                            }
                            break;
                        case 'image/gif':
                            if (function_exists('imagecreatefromgif')) {
                                $image = imagecreatefromgif($original_file_path);
                            }
                            break;
                        case 'image/avif':
                            // 优先使用 Imagick 处理 AVIF
                            if (class_exists('Imagick')) {
                                $image = new Imagick($original_file_path);
                                $image->setImageFormat('webp');
                                $image->writeImage($webp_file_path);
                                $image->clear();
                                $image->destroy();
                                $image = null; // 已经转换,所以后续无需 GD 再转换
                            } elseif (function_exists('exec')) {
                                exec("ffmpeg -i " . escapeshellarg($original_file_path) . " -q:v 100 " . escapeshellarg($webp_file_path));
                            }
                            break;
                        case 'image/tiff':
                            if (class_exists('Imagick')) {
                                $image = new Imagick($original_file_path);
                                $image->setImageFormat('webp');
                                $image->writeImage($webp_file_path);
                                $image->clear();
                                $image->destroy();
                                $image = null;
                            }
                            break;
                        case 'image/x-icon':
                            // ICO:使用 imagecreatefromstring 读取文件内容
                            $data = file_get_contents($original_file_path);
                            $image = imagecreatefromstring($data);
                            break;
                        case 'image/svg+xml':
                            // 使用 rsvg-convert 命令将 SVG 转换为 WebP(请确保服务器上安装了 rsvg-convert)
                            if (function_exists('exec')) {
                                exec("rsvg-convert " . escapeshellarg($original_file_path) . " -f webp -o " . escapeshellarg($webp_file_path));
                            }
                            break;
                        default:
                            // 其它格式尝试使用 imagecreatefromstring 做通用处理
                            $data = file_get_contents($original_file_path);
                            $image = imagecreatefromstring($data);
                            break;
                    }

                    // 若通过 GD 库加载,执行转换
                    if ($image && function_exists('imagewebp')) {
                        imagewebp($image, $webp_file_path, 100); // 100表示质量值,可根据需求调整
                        imagedestroy($image);
                        unlink($original_file_path);
                    }

                    if (!file_exists($webp_file_path)) {
                        $post->post_content = str_replace($src, $default_image_url, $post->post_content);
                        continue;
                    }

                    // 替换文章内容中的图片 URL 为转换后的 WebP 图片 URL
                    $post->post_content = str_replace($src, $wp_upload_dir['url'] . '/' . basename($webp_file_path), $post->post_content);
                    $attachment = ecp_get_attachment_post(basename($webp_file_path), $wp_upload_dir['url'] . '/' . basename($webp_file_path));
                    $attach_id = wp_insert_attachment($attachment, ltrim($wp_upload_dir['subdir'] . '/' . basename($webp_file_path), '/'), 0);
                    $attach_data = wp_generate_attachment_metadata($attach_id, $webp_file_path);
                    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));

            // 刷新 Redis 对象缓存,以避免不立即生效
            wp_cache_delete($post_id, 'posts');
            wp_cache_delete($post_id, 'post_meta');
            clean_post_cache($post_id);
        }
    }
}

function ecp_get_attachment_post($filename, $url) {
    return array(
        'guid'           => $url,
        'post_type'      => 'attachment',
        'post_mime_type' => 'image/webp',
        'post_title'     => preg_replace('/\.[^.]+$/', '', $filename),
        'post_content'   => '',
        'post_status'    => 'inherit'
    );
}

add_action('save_post', 'ecp_save_post', 120, 2);

以上代码功能:

  • 自动将外链图像下载至本地
  • 将下载后的外链图像转换为WebP格式
  • 若图像处理失败,则显示默认图像
  • 将处理后的文件重命名为随机字符串
  • 在执行完成后刷新posts、post_meta对象缓存
  • 使用jsproxy加速下载,同时以隐藏服务器IP地址

PS:以上代码请根据需求自行更改

使用方法

将上述代码复制至 WordPress 的 Functions 中,即可实现相应功能,具体方式在这里不过多赘述,以后单独写一篇文章详细讲解

参考

请登录后发表评论

    没有回复内容