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

PHP GD 圖片處理類:生成多規格縮略圖、水印、裁剪

     編輯:PHP 相關知識

功能特性

  • 支持 JPEG/PNG/GIF/Webp/BMP
  • 等比縮放、固定尺寸裁剪(居中/智能)
  • 圖片水印(位置、透明度、平鋪)
  • 文字水印(字體、大小、顏色、角度)
  • 自動按日期/MD5 分目錄存儲,防衝突命名
  • GIF 動圖水印幀級處理

核心類結構

<?php
namespace App\Utils;

class ImageProcessor {
    private string $srcPath;
    private array  $info = [];      // [width, height, type, mime]
    private resource $srcImg = null;

    public function __construct(string $sourcePath) {
        if (!is_file($sourcePath)) throw new \InvalidArgumentException("File not found: $sourcePath");
        $this->srcPath = $sourcePath;
        $this->loadInfo();
    }

    private function loadInfo(): void {
        $this->info = getimagesize($this->srcPath);
        if (!$this->info) throw new \RuntimeException('Not a valid image');
        [$this->info['width'], $this->info['height'], $this->info['type']] = $this->info;
        $this->info['mime'] = image_type_to_mime_type($this->info['type']);
    }

    private function createSource(): void {
        if ($this->srcImg) return;
        $create = 'imagecreatefrom' . ($this->info['type'] === IMAGETYPE_JPEG ? 'jpeg' : image_type_to_extension($this->info['type'], false));
        $this->srcImg = $create($this->srcPath);
        if (!$this->srcImg) throw new \RuntimeException('Failed to create image resource');
    }

    // ===== 公共 API =====
    /**
     * 生成縮略圖
     * @param int $maxW 最大寬
     * @param int $maxH 最大高
     * @param int $mode 0=等比縮放(含邊框), 1=裁剪填滿, 2=等比縮放(無邊框)
     * @return string 保存路徑
     */
    public function thumb(int $maxW, int $maxH, int $mode = 0, string $saveDir = ''): string {
        $this->createSource();
        $srcW = $this->info['width'];
        $srcH = $this->info['height'];

        // 計算目標尺寸與裁剪座標
        if ($mode === 1) { // 裁剪填滿
            $scale = max($maxW / $srcW, $maxH / $srcH);
            $dstW = $maxW;
            $dstH = $maxH;
            $srcX = ($srcW - $dstW / $scale) / 2;
            $srcY = ($srcH - $dstH / $scale) / 2;
            $srcW = $dstW / $scale;
            $srcH = $dstH / $scale;
        } elseif ($mode === 0) { // 等比含邊框
            $scale = min($maxW / $srcW, $maxH / $srcH);
            $dstW = (int)($srcW * $scale);
            $dstH = (int)($srcH * $scale);
            $srcX = $srcY = 0;
        } else { // 等比無邊框
            $scale = min($maxW / $srcW, $maxH / $srcH);
            $dstW = (int)($srcW * $scale);
            $dstH = (int)($srcH * $scale);
            $srcX = $srcY = 0;
        }

        $dstImg = imagecreatetruecolor($dstW, $dstH);
        // 保持透明度
        if (in_array($this->info['type'], [IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_WEBP])) {
            imagealphablending($dstImg, false);
            imagesavealpha($dstImg, true);
            $transparent = imagecolorallocatealpha($dstImg, 0, 0, 0, 127);
            imagefill($dstImg, 0, 0, $transparent);
        }

        imagecopyresampled($dstImg, $this->srcImg, 0, 0, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH);

        return $this->save($dstImg, $saveDir, "thumb_{$maxW}x{$maxH}");
    }

    /**
     * 添加圖片水印
     * @param string $waterPath 水印圖片路徑
     * @param int $position 0-8 九宮格位置
     * @param int $alpha 0-100 透明度
     * @return string
     */
    public function watermark(string $waterPath, int $position = 4, int $alpha = 50): string {
        $this->createSource();
        $waterInfo = getimagesize($waterPath);
        if (!$waterInfo) throw new \InvalidArgumentException('Invalid watermark image');

        $waterImg = ('imagecreatefrom' . ($waterInfo[2] === IMAGETYPE_JPEG ? 'jpeg' : image_type_to_extension($waterInfo[2], false)))($waterPath);
        $wW = $waterInfo[0];
        $wH = $waterInfo[1];

        // 計算位置
        $srcW = $this->info['width'];
        $srcH = $this->info['height'];
        $positions = [
            0 => [0, 0],                    // 左上
            1 => [$srcW - $wW, 0],          // 右上
            2 => [0, $srcH - $wH],          // 左下
            3 => [$srcW - $wW, $srcH - $wH],// 右下
            4 => [($srcW - $wW) / 2, ($srcH - $wH) / 2], // 居中
            5 => [($srcW - $wW) / 2, 0],    // 上中
            6 => [($srcW - $wW) / 2, $srcH - $wH], // 下中
            7 => [0, ($srcH - $wH) / 2],    // 左中
            8 => [$srcW - $wW, ($srcH - $wH) / 2], // 右中
        ];
        [$dstX, $dstY] = $positions[$position] ?? $positions[4];

        // 複製水印(支持透明通道)
        imagealphablending($waterImg, true);
        imagecopymerge($this->srcImg, $waterImg, $dstX, $dstY, 0, 0, $wW, $wH, $alpha);

        return $this->save($this->srcImg, '', 'watermarked');
    }

    /**
     * 添加文字水印
     */
    public function textWatermark(string $text, string $fontPath, int $size = 20, array $color = [255,255,255], int $alpha = 50, int $angle = 0, int $position = 4): string {
        $this->createSource();
        $box = imagettfbbox($size, $angle, $fontPath, $text);
        $tW = $box[2] - $box[0];
        $tH = $box[1] - $box[7];

        $srcW = $this->info['width'];
        $srcH = $this->info['height'];
        // 位置計算同 watermark()...
        // 簡化:居中
        $x = ($srcW - $tW) / 2;
        $y = ($srcH + $tH) / 2;

        $col = imagecolorallocatealpha($this->srcImg, $color[0], $color[1], $color[2], 127 - (int)($alpha * 1.27));
        imagettftext($this->srcImg, $size, $angle, $x, $y, $col, $fontPath, $text);

        return $this->save($this->srcImg, '', 'text_watermarked');
    }

    // ===== 內部保存 =====
    private function save(resource $img, string $saveDir, string $suffix): string {
        $ext = image_type_to_extension($this->info['type'], false);
        $ext = $ext === 'jpeg' ? 'jpg' : $ext;

        $baseName = pathinfo($this->srcPath, PATHINFO_FILENAME);
        $fileName = $baseName . '_' . $suffix . '.' . $ext;

        $dir = $saveDir ?: dirname($this->srcPath);
        $dir = rtrim($dir, '/') . '/';
        if (!is_dir($dir)) mkdir($dir, 0755, true);

        $fullPath = $dir . $fileName;
        $saveFunc = 'image' . ($ext === 'jpg' ? 'jpeg' : $ext);
        $saveFunc($img, $fullPath, $ext === 'jpg' ? 90 : ($ext === 'png' ? 9 : 0));

        imagedestroy($img);
        return $fullPath;
    }

    public function __destruct() {
        if ($this->srcImg) imagedestroy($this->srcImg);
    }
}

使用示例

require 'vendor/autoload.php';
use App\Utils\ImageProcessor;

$processor = new ImageProcessor('uploads/original/photo.jpg');

// 1. 生成三種規格縮略圖
$small  = $processor->thumb(100, 100, 1);   // 裁剪 100x100
$medium = $processor->thumb(400, 300, 0);   // 等比 400x300 內
$large  = $processor->thumb(800, 600, 2);   // 等比 800x600 內

// 2. 添加圖片水印(右下角,透明度 30%)
$watermarked = $processor->watermark('assets/watermark.png', 3, 30);

// 3. 添加文字水印
$texted = $processor->textWatermark('© MySite', 'fonts/msyh.ttc', 24, [255,255,255], 50, 0, 4);

echo "完成:$small, $medium, $large, $watermarked, $texted";

GIF 動圖特殊處理提示

GD 原生不支持 GIF 幀合成。需引入 `gifsicle` 命令行工具或 `php-gif-encoder` 庫逐幀處理:


// 簡易調用 gifsicle(需服務器安裝)
exec("gifsicle --multifile -O3 -i input.gif -o output.gif --watermark watermark.png --position bottom-right");

性能建議

  • 大圖處理設置 `ini_set('memory_limit', '512M')` 並 `set_time_limit(0)`。
  • 高併發場景將圖片處理推入隊列(Redis/RabbitMQ),Worker 異步執行。
  • 頻繁訪問的縮略圖配合 Nginx `try_files` 直接服務靜態文件,PHP 僅首次生成。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved