首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

百度 UEditor 編輯器圖片上傳加水印完整實現方案

     編輯:PHP 綜合知識

需求背景與架構分析

UEditor 後端預設不支援上傳圖片自動加水印。原文採用修改核心類 Uploader.class.php 並硬編碼水印參數的方式,存在侵入性強、升級困難、配置硬編碼、路徑耦合等工程化問題。

非侵入式擴充架構設計

1. 策略介面定義

interface WatermarkStrategyInterface {
    public function apply(string $sourcePath, string $targetPath, array $config): bool;
    public function supports(string $mimeType): bool;
}

2. GD 文字水印實現

class GdTextWatermark implements WatermarkStrategyInterface {
    public function apply(string $source, string $target, array $config): bool {
        $image = $this->createImageFromFile($source);
        if (!$image) return false;

        $font = $config['font_path'] ?? __DIR__ . '/fonts/msyh.ttc';
        $text = $config['text'] ?? 'Copyright';
        $color = $config['color'] ?? [255, 0, 0];
        $size = $config['size'] ?? 20;
        $angle = $config['angle'] ?? 0;
        $opacity = $config['opacity'] ?? 50;
        $position = $config['position'] ?? 'bottom-right';
        $margin = $config['margin'] ?? 20;

        $box = imagettfbbox($size, $angle, $font, $text);
        $textW = $box[2] - $box[0];
        $textH = $box[7] - $box[1];

        [$x, $y] = $this->calcPosition($position, imagesx($image), imagesy($image), $textW, $textH, $margin);

        $colorAllocated = imagecolorallocatealpha($image, ...$color, 127 - (int)($opacity * 1.27));
        imagettftext($image, $size, $angle, $x, $y, $colorAllocated, $font, $text);

        return $this->saveImage($image, $target, $source);
    }
    // ... 私有輔助方法:createImageFromFile, calcPosition, saveImage
}

3. GD 圖片水印實現

class GdImageWatermark implements WatermarkStrategyInterface {
    public function apply(string $source, string $target, array $config): bool {
        $main = $this->createImageFromFile($source);
        $watermark = $this->createImageFromFile($config['watermark_image']);
        if (!$main || !$watermark) return false;

        $position = $config['position'] ?? 'bottom-right';
        $opacity = $config['opacity'] ?? 50;
        $margin = $config['margin'] ?? 20;

        [$x, $y] = $this->calcPosition($position, imagesx($main), imagesy($main), imagesx($watermark), imagesy($watermark), $margin);

        if ($config['blend'] ?? true) {
            imagecopymerge($main, $watermark, $x, $y, 0, 0, imagesx($watermark), imagesy($watermark), $opacity);
        } else {
            imagecopy($main, $watermark, $x, $y, 0, 0, imagesx($watermark), imagesy($watermark));
        }
        return $this->saveImage($main, $target, $source);
    }
}

4. ImageMagick 高效能實現(推薦生產環境)

class ImagickWatermark implements WatermarkStrategyInterface {
    public function apply(string $source, string $target, array $config): bool {
        try {
            $img = new \Imagick($source);
            $wm = new \Imagick($config['watermark_image']);
            $wm->evaluateImage(\Imagick::EVALUATE_MULTIPLY, $config['opacity'] / 100, \Imagick::CHANNEL_ALPHA);
            $img->compositeImage($wm, \Imagick::COMPOSITE_OVER, ...$this->calcPosition($config, $img, $wm));
            $img->writeImage($target);
            return true;
        } catch (\ImagickException $e) {
            error_log("Watermark failed: {$e->getMessage()}");
            return false;
        }
    }
}

UEditor 整合:事件監聽器模式

// config.json 新增配置段
{
    "imageWatermark": {
        "enabled": true,
        "strategy": "imagick", // gd_text, gd_image, imagick
        "config": {
            "watermark_image": "/static/watermark/logo.png",
            "position": "bottom-right",
            "opacity": 30,
            "margin": 10
        }
    }
}

// 事件監聽器(不修改核心類)
class UEditorWatermarkListener {
    public function onUploadComplete(array $fileInfo, array $config): void {
        if (empty($config['imageWatermark']['enabled'])) return;
        if (!in_array($fileInfo['mime'], ['image/jpeg', 'image/png', 'image/gif', 'image/webp'])) return;

        $strategy = $this->resolveStrategy($config['imageWatermark']['strategy']);
        $strategy->apply($fileInfo['path'], $fileInfo['path'], $config['imageWatermark']['config']);
    }
}

配置管理最佳實踐

// config/watermark.php
return [
    'default' => env('WATERMARK_DRIVER', 'imagick'),
    'drivers' => [
        'gd_text' => [
            'class' => GdTextWatermark::class,
            'font_path' => base_path('resources/fonts/msyh.ttc'),
            'text' => env('WATERMARK_TEXT', '© MySite'),
            'color' => [255, 255, 255],
            'size' => 18,
            'opacity' => 40,
        ],
        'imagick' => [
            'class' => ImagickWatermark::class,
            'watermark_image' => public_path('watermark/logo.png'),
            'position' => 'bottom-right',
            'opacity' => 25,
        ],
    ],
    'positions' => [
        'top-left' => [10, 10],
        'top-right' => ['width - wm_width - 10', 10],
        'center' => ['(width - wm_width) / 2', '(height - wm_height) / 2'],
        'bottom-right' => ['width - wm_width - 10', 'height - wm_height - 10'],
    ],
];

效能與安全考量

  • 非同步處理: 上傳回應前僅存檔,水印處理推送至佇列,避免阻塞使用者請求
  • 原圖保留: 水印版另存為 filename_wm.ext,保留原圖供後續調整
  • 記憶體限制: GD 處理大圖易 OOM,ImageMagick 串流處理更穩健,建議限制上傳尺寸(如 20MB/4000px)
  • 格式支援: WebP、AVIF 等新格式需確認 GD/ImageMagick 版本支援度

總結

採用策略模式 + 事件監聽器 + 配置驅動的架構,實現零侵入式水印擴充。ImageMagick 為首選引擎,配合佇列非同步處理與原圖保留策略,滿足高併發、可維護、可擴充的生產級需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved