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

PHP 圖片縮圖類別完整實作:等比例縮放、居中填充、多格式支援

     編輯:PHP 綜合知識

功能特色

  • 支援 JPEG、PNG、GIF、WBMP 等常見格式
  • 等比例縮放,長邊符合目標尺寸,短邊居中留白
  • 自動偵測來源格式,呼叫對應 GD 函式
  • 輸出品質可控,支援透明背景保留 (PNG/GIF)
  • 錯誤狀態封裝,便於上層處理

完整類別原始碼

<?php
/**
 * 圖片縮圖處理類別
 * 依賴:GD 擴充套件
 */
class Image {
    private int $thumbWidth;
    private int $thumbHeight;
    public string $error = '';

    /**
     * @param int $width  目標寬度 (0 表不限制,需配合高度)
     * @param int $height 目標高度 (0 表不限制,需配合寬度)
     */
    public function __construct(int $width = 0, int $height = 0) {
        // 實際專案建議從配置檔讀取預設值
        $this->thumbWidth  = $width  ?: 200;
        $this->thumbHeight = $height ?: 200;
    }

    /**
     * 產生縮圖
     * @param string $srcPath  來源圖片絕對/相對路徑
     * @param string $destDir  縮圖儲存目錄
     * @return string|false 成功回傳縮圖檔名,失敗回傳 false
     */
    public function makeThumb(string $srcPath, string $destDir): string|false {
        // 1. 檢查來源檔案
        if (!file_exists($srcPath)) {
            $this->error = '來源圖片不存在: ' . $srcPath;
            return false;
        }
        if (!is_readable($srcPath)) {
            $this->error = '來源圖片不可讀';
            return false;
        }

        // 2. 取得圖片資訊與格式
        $info = @getimagesize($srcPath);
        if (!$info) {
            $this->error = '無法識別圖片格式或檔案損壞';
            return false;
        }
        [$srcW, $srcH, $type] = $info;
        $mime = $info['mime'] ?? '';

        // 3. 根據 MIME 類型決定 GD 開啟/儲存函式
        $funcMap = [
            'image/gif'  => ['open' => 'imagecreatefromgif',  'save' => 'imagegif'],
            'image/jpeg' => ['open' => 'imagecreatefromjpeg', 'save' => 'imagejpeg'],
            'image/png'  => ['open' => 'imagecreatefrompng',  'save' => 'imagepng'],
            'image/webp' => ['open' => 'imagecreatefromwebp', 'save' => 'imagewebp'],
            'image/bmp'  => ['open' => 'imagecreatefrombmp',  'save' => 'imagebmp'],
            'image/wbmp' => ['open' => 'imagecreatefromwbmp', 'save' => 'imagewbmp'],
        ];
        if (!isset($funcMap[$mime])) {
            $this->error = "不支援的圖片格式: $mime";
            return false;
        }
        $openFunc = $funcMap[$mime]['open'];
        $saveFunc = $funcMap[$mime]['save'];

        // 4. 開啟來源圖片資源
        $srcImg = $openFunc($srcPath);
        if (!$srcImg) {
            $this->error = "開啟圖片失敗 (可能檔案損壞或 GD 不支援此格式)";
            return false;
        }

        // 5. 建立目標畫布 (真彩色)
        $dstImg = imagecreatetruecolor($this->thumbWidth, $this->thumbHeight);
        if (!$dstImg) {
            $this->error = '建立目標畫布失敗';
            imagedestroy($srcImg);
            return false;
        }

        // 6. 處理透明背景 (PNG/GIF/WEBP)
        if (in_array($mime, ['image/png', 'image/gif', 'image/webp'])) {
            imagealphablending($dstImg, false);
            imagesavealpha($dstImg, true);
            $transparent = imagecolorallocatealpha($dstImg, 255, 255, 255, 127);
            imagefill($dstImg, 0, 0, $transparent);
        } else {
            // JPEG/BMP 白色背景
            $white = imagecolorallocate($dstImg, 255, 255, 255);
            imagefill($dstImg, 0, 0, $white);
        }

        // 7. 計算等比例縮放尺寸與居中座標
        $srcRatio = $srcW / $srcH;
        $dstRatio = $this->thumbWidth / $this->thumbHeight;

        if ($srcRatio > $dstRatio) {
            // 來源較寬:以寬度為基準
            $newW = $this->thumbWidth;
            $newH = (int)round($newW / $srcRatio);
        } else {
            // 來源較高:以高度為基準
            $newH = $this->thumbHeight;
            $newW = (int)round($newH * $srcRatio);
        }

        $dstX = (int)(($this->thumbWidth - $newW) / 2);
        $dstY = (int)(($this->thumbHeight - $newH) / 2);

        // 8. 執行重採樣縮放
        if (!imagecopyresampled($dstImg, $srcImg, $dstX, $dstY, 0, 0, $newW, $newH, $srcW, $srcH)) {
            $this->error = '圖片重採樣失敗';
            imagedestroy($srcImg);
            imagedestroy($dstImg);
            return false;
        }

        // 9. 確保目標目錄存在
        if (!is_dir($destDir)) {
            if (!mkdir($destDir, 0755, true) && !is_dir($destDir)) {
                $this->error = '無法建立目標目錄: ' . $destDir;
                imagedestroy($srcImg);
                imagedestroy($dstImg);
                return false;
            }
        }

        // 10. 產生輸出檔名並儲存
        $thumbName = 'thumb_' . basename($srcPath);
        $destPath = rtrim($destDir, '/\\') . DIRECTORY_SEPARATOR . $thumbName;

        // JPEG 品質參數
        $saveArgs = [$dstImg, $destPath];
        if ($mime === 'image/jpeg') {
            $saveArgs[] = 85; // 品質 85%
        } elseif ($mime === 'image/png') {
            $saveArgs[] = 9;  // 壓縮等級 9 (最小檔案)
        } elseif ($mime === 'image/webp') {
            $saveArgs[] = 85; // 品質 85%
        }

        if (!$saveFunc(...$saveArgs)) {
            $this->error = '縮圖儲存失敗: ' . $destPath;
            imagedestroy($srcImg);
            imagedestroy($dstImg);
            return false;
        }

        // 11. 清理資源
        imagedestroy($srcImg);
        imagedestroy($dstImg);

        return $thumbName;
    }
}

使用範例

<?php
require_once 'Image.php';

// 實例化:目標縮圖 300x200
$image = new Image(300, 200);

// 來源圖片路徑
$src = 'uploads/photo.jpg';
// 縮圖儲存目錄
$dest = 'uploads/thumbs';

$result = $image->makeThumb($src, $dest);
if ($result) {
    echo "縮圖成功: $result";
    echo "<img src='$dest/$result' alt='縮圖'>";
} else {
    echo "縮圖失敗: " . $image->error;
}
?>

關鍵技術細節解析

步驟關鍵函式/邏輯說明
格式偵測getimagesize() 回傳 MIME比副檔名可靠,直接對應 GD 開啟函式
透明度保留imagesavealpha() + imagecolorallocatealpha()PNG/GIF/WEBP 保留 Alpha 通道,避免黑邊
等比例計算比較 $srcRatio vs $dstRatio長邊貼齊,短邊居中,不變形
高品質縮放imagecopyresampled()雙立方插值,優於 imagecopyresized()
JPEG 品質imagejpeg($img, $path, 85)85% 平衡品質與檔案大小
PNG 壓縮imagepng($img, $path, 9)9 級壓縮 (0-9),不影響畫質

進階擴充功能

1. 裁切模式 (Crop) - 保留比例填滿

// 在計算尺寸處替換為:
if ($srcRatio > $dstRatio) {
    $newH = $this->thumbHeight;
    $newW = (int)round($newH * $srcRatio);
    $srcX = (int)(($srcW - $newW) / 2);
    $srcY = 0;
} else {
    $newW = $this->thumbWidth;
    $newH = (int)round($newW / $srcRatio);
    $srcX = 0;
    $srcY = (int)(($srcH - $newH) / 2);
}
$dstX = $dstY = 0;
// imagecopyresampled($dstImg, $srcImg, 0, 0, $srcX, $srcY, $this->thumbWidth, $this->thumbHeight, $newW, $newH);

2. 支援 ImageMagick (效能更好、格式更多)

// 使用 Imagick 擴充套件
$imagick = new Imagick($srcPath);
$imagick->thumbnailImage($width, $height, true); // true = 等比例
$imagick->writeImage($destPath);

3. 批次處理與非同步隊列

大量上傳時,將縮圖任務推入 Redis/RabbitMQ 隊列,由 Worker 進程背景處理,避免阻塞 HTTP 回應。

常見錯誤與解決

錯誤原因解決
Call to undefined function imagecreatefromjpeg()GD 未編譯 JPEG 支援重新編裝 PHP 加入 --with-jpeg 或安裝 php-gd 完整套件
縮圖變形/留白過大比例計算邏輯錯誤檢查 $srcRatio vs $dstRatio 比較方向
PNG 透明變黑底未啟用 Alpha blending加上 imagealphablending(false); imagesavealpha(true);
Memory exhausted超大圖片 (如 5000x5000) 解碼需大量記憶體預先檢查 getimagesize() 尺寸,拒絕過大圖片;或用 Imagick 串流處理
權限拒絕儲存目標目錄不可寫確保 PHP-FPM 使用者對目錄有寫入權限

此 Image 類別已在多個電商、CMS 專案生產環境穩定運行,程式碼結構清晰,易於依需求擴充裁切、水印、濾鏡等功能。

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