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

PHP 圖片等比例縮放實作:GD 函式庫、品質控制、多格式支援

     編輯:PHP 相關知識

前言

圖片上傳後常需產生縮圖或壓縮版本。PHP GD 擴充套件提供 imagecopyresampled() 高品質重採樣。本文提供一個完整的 ImageResizer 類別,支援 等比例縮放、最大寬高限制、JPEG 品質調整、PNG 透明度保留、GIF 動畫首幀處理、輸出到檔案或瀏覽器。

完整類別實作

<?php
class ImageResizer {
    private $sourcePath;
    private $image;
    private $width;
    private $height;
    private $mime;
    private $type; // IMAGETYPE_* 常數

    public function __construct(string $sourcePath) {
        if (!file_exists($sourcePath)) {
            throw new InvalidArgumentException("檔案不存在:{$sourcePath}");
        }
        $this->sourcePath = $sourcePath;
        $this->loadImage();
    }

    private function loadImage(): void {
        $info = getimagesize($this->sourcePath);
        $this->width = $info[0];
        $this->height = $info[1];
        $this->type = $info[2];
        $this->mime = $info['mime'];

        switch ($this->type) {
            case IMAGETYPE_JPEG:
                $this->image = imagecreatefromjpeg($this->sourcePath);
                break;
            case IMAGETYPE_PNG:
                $this->image = imagecreatefrompng($this->sourcePath);
                break;
            case IMAGETYPE_GIF:
                $this->image = imagecreatefromgif($this->sourcePath);
                break;
            case IMAGETYPE_WEBP:
                $this->image = imagecreatefromwebp($this->sourcePath);
                break;
            default:
                throw new RuntimeException("不支援的圖片類型:{$this->mime}");
        }
    }

    /**
     * 等比例縮放至最大寬高範圍內
     * @param int $maxWidth  最大寬度
     * @param int $maxHeight 最大高度
     * @return self 鏈式呼叫
     */
    public function resize(int $maxWidth, int $maxHeight): self {
        $ratio = min($maxWidth / $this->width, $maxHeight / $this->height);
        
        // 若原圖已小於限制,不放大
        if ($ratio >= 1) return $this;

        $newWidth = (int)round($this->width * $ratio);
        $newHeight = (int)round($this->height * $ratio);

        $dst = imagecreatetruecolor($newWidth, $newHeight);

        // PNG/GIF 保留透明度
        if ($this->type === IMAGETYPE_PNG || $this->type === IMAGETYPE_GIF) {
            imagealphablending($dst, false);
            imagesavealpha($dst, true);
            $transparent = imagecolorallocatealpha($dst, 255, 255, 255, 127);
            imagefilledrectangle($dst, 0, 0, $newWidth, $newHeight, $transparent);
        }

        // 高品質重採樣
        imagecopyresampled($dst, $this->image, 0, 0, 0, 0, $newWidth, $newHeight, $this->width, $this->height);
        
        imagedestroy($this->image);
        $this->image = $dst;
        $this->width = $newWidth;
        $this->height = $newHeight;
        return $this;
    }

    /**
     * 裁切為正方形(從中心)
     */
    public function cropSquare(): self {
        $size = min($this->width, $this->height);
        $x = ($this->width - $size) / 2;
        $y = ($this->height - $size) / 2;
        $dst = imagecreatetruecolor($size, $size);
        imagecopyresampled($dst, $this->image, 0, 0, $x, $y, $size, $size, $size, $size);
        imagedestroy($this->image);
        $this->image = $dst;
        $this->width = $this->height = $size;
        return $this;
    }

    /**
     * 儲存至檔案
     * @param string $destPath 目的路徑
     * @param int $quality JPEG/WebP 品質 0-100
     * @param int $pngCompression PNG 壓縮等級 0-9
     */
    public function save(string $destPath, int $quality = 85, int $pngCompression = 6): void {
        $dir = dirname($destPath);
        if (!is_dir($dir)) mkdir($dir, 0755, true);

        switch ($this->type) {
            case IMAGETYPE_JPEG:
                imagejpeg($this->image, $destPath, $quality);
                break;
            case IMAGETYPE_PNG:
                imagepng($this->image, $destPath, $pngCompression);
                break;
            case IMAGETYPE_GIF:
                imagegif($this->image, $destPath);
                break;
            case IMAGETYPE_WEBP:
                imagewebp($this->image, $destPath, $quality);
                break;
        }
    }

    /**
     * 直接輸出到瀏覽器(附檔名下載或內嵌顯示)
     */
    public function output(bool $download = false, string $filename = ''): void {
        if ($download) {
            header('Content-Disposition: attachment; filename="' . ($filename ?: basename($this->sourcePath)) . '"');
        }
        header('Content-Type: ' . $this->mime);
        header('Cache-Control: public, max-age=31536000');
        
        switch ($this->type) {
            case IMAGETYPE_JPEG: imagejpeg($this->image, null, 85); break;
            case IMAGETYPE_PNG:  imagepng($this->image, null, 6); break;
            case IMAGETYPE_GIF:  imagegif($this->image); break;
            case IMAGETYPE_WEBP: imagewebp($this->image, null, 85); break;
        }
        exit;
    }

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

使用範例

<?php
require 'ImageResizer.php';

// 1. 產生縮圖(最大 300x300)
$resizer = new ImageResizer('uploads/photo.jpg');
$resizer->resize(300, 300)
        ->save('uploads/thumbs/photo_thumb.jpg', 80);

// 2. 產生正方形頭像(裁切 + 縮放)
$avatar = new ImageResizer('uploads/avatar.png');
$avatar->cropSquare()
       ->resize(200, 200)
       ->save('uploads/avatars/user_123.png');

// 3. 直接輸出到瀏覽器(動態縮圖 API)
// 路由:/api/thumb?file=photo.jpg&w=300&h=200
$file = $_GET['file'] ?? '';
$w = (int)($_GET['w'] ?? 300);
$h = (int)($_GET['h'] ?? 300);
$resizer = new ImageResizer("uploads/{$file}");
$resizer->resize($w, $h)->output(); // 內嵌顯示
// $resizer->resize($w, $h)->output(true, "thumb_{$file}"); // 強制下載

進階議題:大圖處理與記憶體優化

問題解決方案
超大圖(>5000px)導致記憶體耗盡1. 增加 memory_limit
2. 使用 Imagick 串流處理
3. 分塊處理(GD 不支援,需外部工具)
GIF 動畫縮放後只剩首幀GD 不支援多幀 GIF,需用 Imagick 或 gifsicle CLI
PNG 透明背景變黑確呼叫 imagesavealpha($dst, true) 並關閉 alpha blending
JPEG 方向標籤(EXIF Orientation)導致旋轉錯誤上傳前用 exif_read_data() 讀取 Orientation 並物理旋轉修正

GD vs Imagick 選擇指南

  • GD:內建、無外部依賴、支援基本縮放/裁切/水印、記憶體佔用較大、不支援多幀 GIF、WebP 支援較新版本才有。
  • Imagick:功能豐富(濾鏡、色彩空間、多幀、PDF 轉圖)、串流處理大圖不佔記憶體、需安裝 ImageMagick + PHP 擴充套件、部署較複雜。

結語

ImageResizer 類別封裝了 GD 圖片處理的最佳實踐:等比例縮放、透明度保留、品質參數化、鏈式 API、資源自動釋放。實務專案中,上傳流程建議整合「上傳 → 暫存 → 異步佇列(產生多種尺寸縮圖/水印/轉 WebP) → 入庫記錄」,避免同步處理阻塞使用者請求。

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