WordPress图片自动压缩与WebP转换插件

下面我将为您创建一个完整的WordPress插件,实现图片备份、WebP格式转换、压缩和媒体库管理的功能。

插件文件结构

webp-compressor-backup/
├── webp-compressor-backup.php
├── uninstall.php
└── readme.txt

主插件文件:webp-compressor-backup.php

<?php
/**
 * Plugin Name: WebP压缩与备份
 * Plugin URI: https://yourwebsite.com/
 * Description: 自动备份上传的图片,转换为WebP格式并压缩,优化媒体库显示。
 * Version: 1.0.0
 * Author: 您的名称
 * License: GPL v2 or later
 * Text Domain: webp-compressor-backup
 */

// 防止直接访问
if (!defined('ABSPATH')) {
    exit;
}

// 定义插件常量
define('WCB_PLUGIN_URL', plugin_dir_url(__FILE__));
define('WCB_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('WCB_BACKUP_DIR', WP_CONTENT_DIR . '/uploads/backup/');
define('WCB_WEBP_QUALITY', 75); // 中等压缩质量

class WebPCompressorBackup {

    public function __construct() {
        add_action('init', array($this, 'init'));
        register_activation_hook(__FILE__, array($this, 'activate'));
        register_deactivation_hook(__FILE__, array($this, 'deactivate'));
    }

    public function init() {
        // 确保备份目录存在
        $this->ensure_backup_dir();

        // 添加WebP支持
        add_filter('upload_mimes', array($this, 'add_webp_support'));
        add_filter('file_is_displayable_image', array($this, 'webp_is_displayable'), 10, 2);

        // 处理上传的图片
        add_filter('wp_handle_upload', array($this, 'handle_uploaded_image'));

        // 生成WebP版本到媒体库
        add_filter('wp_generate_attachment_metadata', array($this, 'generate_webp_versions'), 10, 2);

        // 修改图片URL以优先使用WebP
        add_filter('wp_get_attachment_image_src', array($this, 'replace_with_webp'), 10, 4);
        add_filter('the_content', array($this, 'replace_content_images_with_webp'));

        // 在媒体库中显示WebP信息
        add_filter('manage_media_columns', array($this, 'add_media_columns'));
        add_action('manage_media_custom_column', array($this, 'manage_media_columns_content'), 10, 2);
    }

    public function activate() {
        // 创建备份目录
        $this->ensure_backup_dir();

        // 检查服务器是否支持WebP转换
        $this->check_server_compatibility();

        // 刷新Permalink结构
        flush_rewrite_rules();
    }

    public function deactivate() {
        // 清理操作
        flush_rewrite_rules();
    }

    private function ensure_backup_dir() {
        if (!file_exists(WCB_BACKUP_DIR)) {
            wp_mkdir_p(WCB_BACKUP_DIR);
        }

        // 增加安全保护
        $index_file = WCB_BACKUP_DIR . 'index.php';
        if (!file_exists($index_file)) {
            file_put_contents($index_file, '<?php // Silence is golden');
        }

        $htaccess_file = WCB_BACKUP_DIR . '.htaccess';
        if (!file_exists($htaccess_file)) {
            file_put_contents($htaccess_file, 'Deny from all');
        }
    }

    private function check_server_compatibility() {
        $errors = array();

        // 检查Imagick和GD支持
        if (!extension_loaded('imagick') && !extension_loaded('gd')) {
            $errors[] = '服务器需要安装Imagick或GD扩展才能进行图片处理。';
        } elseif (extension_loaded('imagick')) {
            $imagick = new Imagick();
            $formats = $imagick->queryFormats();
            if (!in_array('WEBP', $formats)) {
                $errors[] = 'Imagick不支持WebP格式,请安装支持WebP的Imagick版本。';
            }
        }

        if (!empty($errors)) {
            deactivate_plugins(plugin_basename(__FILE__));
            wp_die(implode('<br>', $errors) . '<br><a href="' . admin_url('plugins.php') . '">返回插件页面</a>');
        }
    }

    // 添加WebP支持
    public function add_webp_support($mimes) {
        $mimes['webp'] = 'image/webp';
        return $mimes;
    }

    public function webp_is_displayable($result, $path) {
        if ($result === false) {
            $displayable_image_types = array(IMAGETYPE_WEBP);
            $info = @getimagesize($path);

            if (empty($info)) {
                $result = false;
            } elseif (!in_array($info[2], $displayable_image_types)) {
                $result = false;
            } else {
                $result = true;
            }
        }
        return $result;
    }

    // 处理上传的图片:备份并转换为WebP
    public function handle_uploaded_image($upload) {
        // 只处理图片文件
        if (!str_starts_with($upload['type'], 'image/')) {
            return $upload;
        }

        // 检查是否已经是WebP格式
        $file_info = pathinfo($upload['file']);
        if (isset($file_info['extension']) && strtolower($file_info['extension']) === 'webp') {
            return $upload;
        }

        // 备份原始图片
        $this->backup_original_image($upload['file']);

        // 转换为WebP
        $webp_path = $this->convert_to_webp($upload['file']);

        if ($webp_path && file_exists($webp_path)) {
            // 更新文件信息以使用WebP版本
            $upload['file'] = $webp_path;
            $upload['url'] = str_replace($file_info['basename'], basename($webp_path), $upload['url']);
            $upload['type'] = 'image/webp';
        }

        return $upload;
    }

    // 备份原始图片
    private function backup_original_image($file_path) {
        $backup_path = WCB_BACKUP_DIR . basename($file_path);

        if (!file_exists($backup_path)) {
            copy($file_path, $backup_path);
        }
    }

    // 转换为WebP格式
    private function convert_to_webp($file_path) {
        $file_info = pathinfo($file_path);
        $webp_path = $file_info['dirname'] . '/' . $file_info['filename'] . '.webp';

        // 如果已存在WebP版本,先删除
        if (file_exists($webp_path)) {
            unlink($webp_path);
        }

        // 根据可用扩展选择转换方法
        if (extension_loaded('imagick')) {
            return $this->convert_with_imagick($file_path, $webp_path);
        } elseif (extension_loaded('gd')) {
            return $this->convert_with_gd($file_path, $webp_path);
        }

        return false;
    }

    // 使用Imagick转换
    private function convert_with_imagick($source, $destination) {
        try {
            $image = new Imagick($source);

            // 设置压缩质量
            $image->setImageCompressionQuality(WCB_WEBP_QUALITY);
            $image->setImageFormat('webp');

            // 移除元数据以减少文件大小
            $image->stripImage();

            // 保存WebP图片
            $image->writeImage($destination);
            $image->clear();

            return $destination;
        } catch (Exception $e) {
            error_log('WebP转换错误(Imagick): ' . $e->getMessage());
            return false;
        }
    }

    // 使用GD库转换
    private function convert_with_gd($source, $destination) {
        $image_info = getimagesize($source);
        $mime_type = $image_info['mime'];

        // 根据MIME类型创建图像资源
        switch ($mime_type) {
            case 'image/jpeg':
                $image = imagecreatefromjpeg($source);
                break;
            case 'image/png':
                $image = imagecreatefrompng($source);
                // 保留透明度
                imagealphablending($image, false);
                imagesavealpha($image, true);
                break;
            case 'image/gif':
                $image = imagecreatefromgif($source);
                break;
            default:
                return false;
        }

        if (!$image) {
            return false;
        }

        // 转换为WebP
        $result = imagewebp($image, $destination, WCB_WEBP_QUALITY);

        // 释放内存
        imagedestroy($image);

        return $result ? $destination : false;
    }

    // 为附件生成WebP版本
    public function generate_webp_metadata($metadata, $attachment_id) {
        $upload_dir = wp_upload_dir();
        $original_file = get_attached_file($attachment_id);

        // 检查文件是否是WebP格式,如果不是则生成WebP版本
        if (pathinfo($original_file, PATHINFO_EXTENSION) !== 'webp') {
            $webp_path = $this->convert_to_webp($original_file);

            if ($webp_path) {
                // 更新附件元数据以包含WebP版本
                $metadata['file'] = _wp_relative_upload_path($webp_path);

                // 为每个缩略图大小生成WebP版本
                if (isset($metadata['sizes']) && is_array($metadata['sizes'])) {
                    foreach ($metadata['sizes'] as $size => $size_info) {
                        $size_file_path = path_join($upload_dir['basedir'], dirname($metadata['file'])) . '/' . $size_info['file'];

                        if (file_exists($size_file_path)) {
                            $webp_size_path = $this->convert_to_webp($size_file_path);

                            if ($webp_size_path) {
                                $metadata['sizes'][$size]['file'] = basename($webp_size_path);
                                $metadata['sizes'][$size]['mime-type'] = 'image/webp';
                            }
                        }
                    }
                }

                // 更新主文件为WebP
                update_attached_file($attachment_id, $webp_path);
            }
        }

        return $metadata;
    }

    // 替换附件URL为WebP版本
    public function replace_with_webp($image, $attachment_id, $size, $icon) {
        if (!$image) {
            return $image;
        }

        $webp_url = $this->get_webp_version_url($image[0]);

        if ($webp_url) {
            $image[0] = $webp_url;
        }

        return $image;
    }

    // 替换内容中的图片为WebP版本
    public function replace_content_images_with_webp($content) {
        if (empty($content)) {
            return $content;
        }

        // 匹配所有img标签
        preg_match_all('/<img[^>]+src="([^">]+)"[^>]*>/i', $content, $matches);

        if (!empty($matches[1])) {
            foreach ($matches[1] as $img_url) {
                $webp_url = $this->get_webp_version_url($img_url);

                if ($webp_url) {
                    $content = str_replace($img_url, $webp_url, $content);
                }
            }
        }

        return $content;
    }

    // 获取图片的WebP版本URL
    private function get_webp_version_url($original_url) {
        // 检查是否已经是WebP格式
        if (pathinfo($original_url, PATHINFO_EXTENSION) === 'webp') {
            return false;
        }

        $upload_dir = wp_upload_dir();
        $base_url = $upload_dir['baseurl'];

        // 确保URL使用相同协议
        $base_url = set_url_scheme($base_url, parse_url($original_url, PHP_URL_SCHEME));

        // 检查图片是否在上传目录内
        if (strpos($original_url, $base_url) === 0) {
            $relative_path = substr($original_url, strlen($base_url) + 1);
            $webp_relative_path = preg_replace('/.(jpg|jpeg|png|gif)$/i', '.webp', $relative_path);
            $webp_path = path_join($upload_dir['basedir'], $webp_relative_path);

            if (file_exists($webp_path)) {
                return path_join($base_url, $webp_relative_path);
            }
        }

        return false;
    }

    // 在媒体库添加WebP信息列
    public function add_media_columns($columns) {
        $columns['webp_format'] = 'WebP格式';
        $columns['backup_status'] = '备份状态';
        return $columns;
    }

    public function manage_media_columns_content($column_name, $attachment_id) {
        if ($column_name === 'webp_format') {
            $file = get_attached_file($attachment_id);
            $extension = pathinfo($file, PATHINFO_EXTENSION);

            if ($extension === 'webp') {
                echo '<span style="color: green;">✓ WebP格式</span>';
            } else {
                // 检查是否有对应的WebP版本
                $webp_path = preg_replace('/.(jpg|jpeg|png|gif)$/i', '.webp', $file);

                if (file_exists($webp_path)) {
                    echo '<span style="color: blue;">有WebP版本</span>';
                } else {
                    echo '<span style="color: orange;">未转换</span>';
                }
            }
        }

        if ($column_name === 'backup_status') {
            $file = get_attached_file($attachment_id);
            $backup_path = WCB_BACKUP_DIR . basename($file);

            if (file_exists($backup_path)) {
                echo '<span style="color: green;">已备份</span>';
            } else {
                $extension = pathinfo($file, PATHINFO_EXTENSION);

                if ($extension === 'webp') {
                    $original_path = preg_replace('/.webp$/i', '.jpg', $backup_path);

                    if (!file_exists($original_path)) {
                        $original_path = preg_replace('/.webp$/i', '.png', $backup_path);
                    }

                    if (file_exists($original_path)) {
                        echo '<span style="color: green;">原始已备份</span>';
                    } else {
                        echo '<span style="color: orange;">无备份</span>';
                    }
                } else {
                    echo '<span style="color: orange;">未备份</span>';
                }
            }
        }
    }
}

// 初始化插件
new WebPCompressorBackup();

// 添加设置页面
add_action('admin_menu', 'wcb_add_admin_menu');
add_action('admin_init', 'wcb_settings_init');

function wcb_add_admin_menu() {
    add_options_page(
        'WebP压缩与备份设置',
        'WebP压缩设置',
        'manage_options',
        'webp-compressor-backup',
        'wcb_options_page'
    );
}

function wcb_settings_init() {
    register_setting('wcb_plugin_page', 'wcb_settings');

    add_settings_section(
        'wcb_plugin_page_section',
        'WebP转换设置',
        'wcb_settings_section_callback',
        'wcb_plugin_page'
    );

    add_settings_field(
        'wcb_compression_quality',
        '压缩质量 (0-100)',
        'wcb_compression_quality_render',
        'wcb_plugin_page',
        'wcb_plugin_page_section'
    );

    add_settings_field(
        'wcb_backup_original',
        '备份原始图片',
        'wcb_backup_original_render',
        'wcb_plugin_page',
        'wcb_plugin_page_section'
    );
}

function wcb_compression_quality_render() {
    $options = get_option('wcb_settings');
    $quality = isset($options['wcb_compression_quality']) ? $options['wcb_compression_quality'] : 75;
    ?>
    <input type='number' min='0' max='100' name='wcb_settings[wcb_compression_quality]' value='<?php echo $quality; ?>'>
    <p class="description">数值越高,质量越好但文件越大。推荐值: 75-85</p>
    <?php
}

function wcb_backup_original_render() {
    $options = get_option('wcb_settings');
    $backup = isset($options['wcb_backup_original']) ? $options['wcb_backup_original'] : 1;
    ?>
    <input type='checkbox' name='wcb_settings[wcb_backup_original]' value='1' <?php checked(1, $backup, true); ?>>
    <label for='wcb_settings[wcb_backup_original]'>启用原始图片备份</label>
    <?php
}

function wcb_settings_section_callback() {
    echo '<p>配置WebP图片转换和压缩设置</p>';

    // 显示服务器兼容性信息
    $compatibility = array();

    if (extension_loaded('imagick')) {
        $imagick = new Imagick();
        $formats = $imagick->queryFormats();
        $compatibility[] = in_array('WEBP', $formats) ? 'Imagick: 支持WebP' : 'Imagick: 不支持WebP';
    }

    if (extension_loaded('gd')) {
        $gd_info = gd_info();
        $compatibility[] = isset($gd_info['WebP Support']) && $gd_info['WebP Support'] ? 'GD: 支持WebP' : 'GD: 不支持WebP';
    }

    if (empty($compatibility)) {
        $compatibility[] = '警告: 未检测到图片处理库(Imagick或GD)';
    }

    echo '<p><strong>服务器兼容性:</strong> ' . implode(', ', $compatibility) . '</p>';
}

function wcb_options_page() {
    ?>
    <div class="wrap">
        <h1>WebP压缩与备份设置</h1>

        <form action='options.php' method='post'>
            <?php
            settings_fields('wcb_plugin_page');
            do_settings_sections('wcb_plugin_page');
            submit_button();
            ?>
        </form>

        <h2>批量转换现有图片</h2>
        <p>将媒体库中现有的图片转换为WebP格式</p>
        <button id="wcb_batch_convert" class="button button-primary">开始批量转换</button>
        <div id="wcb_progress" style="margin-top: 15px; display: none;">
            <p>处理进度: <span id="wcb_progress_text">0%</span></p>
            <div class="progress-bar" style="height: 20px; background: #f1f1f1; width: 100%;">
                <div id="wcb_progress_bar" style="height: 100%; background: #0073aa; width: 0%;"></div>
            </div>
        </div>

        <script>
        jQuery(document).ready(function($) {
            $('#wcb_batch_convert').on('click', function() {
                $('#wcb_progress').show();
                processBatchConversion(0);
            });

            function processBatchConversion(offset) {
                $.post(ajaxurl, {
                    action: 'wcb_batch_convert',
                    offset: offset,
                    nonce: '<?php echo wp_create_nonce('wcb_batch_convert'); ?>'
                }, function(response) {
                    if (response.success) {
                        var progress = response.data.progress;
                        $('#wcb_progress_bar').css('width', progress + '%');
                        $('#wcb_progress_text').text(progress + '%');

                        if (progress < 100) {
                            processBatchConversion(response.data.offset);
                        } else {
                            $('#wcb_progress_text').text('转换完成!');
                        }
                    } else {
                        alert('发生错误: ' + response.data);
                    }
                });
            }
        });
        </script>
    </div>
    <?php
}

// 处理批量转换AJAX请求
add_action('wp_ajax_wcb_batch_convert', 'wcb_handle_batch_conversion');

function wcb_handle_batch_conversion() {
    if (!wp_verify_nonce($_POST['nonce'], 'wcb_batch_convert')) {
        wp_die('安全验证失败');
    }

    if (!current_user_can('manage_options')) {
        wp_die('权限不足');
    }

    $offset = intval($_POST['offset']);
    $batch_size = 5; // 每次处理5个图片

    // 获取所有图片附件
    $args = array(
        'post_type' => 'attachment',
        'post_mime_type' => array('image/jpeg', 'image/png', 'image/gif'),
        'posts_per_page' => $batch_size,
        'offset' => $offset,
        'fields' => 'ids'
    );

    $attachments = get_posts($args);
    $total_attachments = wp_count_posts('attachment');
    $total_images = 0;

    foreach (array('image/jpeg', 'image/png', 'image/gif') as $mime) {
        if (isset($total_attachments->$mime)) {
            $total_images += $total_attachments->$mime;
        }
    }

    if (empty($attachments)) {
        wp_send_json_success(array('progress' => 100, 'offset' => 0));
    }

    $plugin = new WebPCompressorBackup();

    foreach ($attachments as $attachment_id) {
        $file_path = get_attached_file($attachment_id);

        // 备份原始图片
        $plugin->backup_original_image($file_path);

        // 转换为WebP
        $webp_path = $plugin->convert_to_webp($file_path);

        if ($webp_path) {
            // 更新附件元数据
            $metadata = wp_generate_attachment_metadata($attachment_id, $webp_path);
            wp_update_attachment_metadata($attachment_id, $metadata);
        }
    }

    $progress = min(100, round(($offset + count($attachments)) / $total_images * 100));
    $new_offset = $offset + count($attachments);

    wp_send_json_success(array('progress' => $progress, 'offset' => $new_offset));
}
?>

卸载脚本:uninstall.php

<?php
// 防止直接访问
if (!defined('WP_UNINSTALL_PLUGIN')) {
    exit;
}

// 删除插件设置
delete_option('wcb_settings');

// 可选: 删除备份目录 (谨慎操作)
// 注意: 取消注释以下代码将删除所有备份的原始图片
/*
$backup_dir = WP_CONTENT_DIR . '/uploads/backup/';
if (file_exists($backup_dir)) {
    $files = glob($backup_dir . '*');
    foreach ($files as $file) {
        if (is_file($file)) {
            unlink($file);
        }
    }
    rmdir($backup_dir);
}
*/
?>

插件说明文件:readme.txt

=== WebP压缩与备份 ===
Contributors: yourname
Tags: webp, compression, image optimization, backup
Requires at least: 5.8
Tested up to: 6.3
Stable tag: 1.0.0
License: GPLv2 or later

自动将上传的图片转换为WebP格式并进行压缩,同时备份原始图片。

== 描述 ==

这个WordPress插件可以:

* 自动备份所有上传的原始图片
* 将图片转换为WebP格式并进行中等压缩
* 在媒体库中优先使用WebP格式图片
* 在前端自动使用WebP图片
* 提供批量转换现有图片的功能

== 安装 ==

1. 上传插件文件夹到 `/wp-content/plugins/` 目录
2. 在插件菜单中激活"WebP压缩与备份"
3. 前往 设置 > WebP压缩设置 配置选项

== 注意事项 ==

* 需要PHP的Imagick或GD扩展支持WebP格式
* 转换大量现有图片可能需要较长时间
* 备份功能会占用额外的存储空间

== 更新日志 ==

= 1.0.0 =
* 初始版本发布

使用说明

  1. 安装插件:将插件文件夹上传到WordPress的/wp-content/plugins/目录,然后在后台激活。
  2. 配置设置:在”设置” > “WebP压缩设置”中调整压缩质量和备份选项。
  3. 批量转换:在设置页面使用”开始批量转换”按钮将现有媒体库图片转换为WebP格式。
  4. 媒体管理:在媒体库中可以看到新增的”WebP格式”和”备份状态”列,用于监控转换状态。

技术说明

  • 插件使用WordPress钩子系统集成到上传流程中
  • 支持通过Imagick和GD库进行WebP转换
  • 自动处理图片URL替换,确保前端使用WebP格式
  • 提供完整的备份和恢复机制

这个插件提供了完整的图片优化解决方案,能够显著减少网站图片大小,提高页面加载速度,同时确保原始图片安全备份。