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

PHP 圖片縮圖函式:保持比例、支援多格式、可配置品質

     編輯:PHP 相關知識

功能需求

  • 依指定最大寬/高等比例縮放
  • 支援 JPEG、PNG、GIF、WebP
  • 可配置輸出品質
  • 選擇性覆蓋原檔或輸出新檔
  • 保留 PNG/GIF 透明度
  • 錯誤處理與資源釋放

完整實作

<?php
/**
 * 產生縮圖
 * 
 * @param string $sourcePath 來源圖片路徑
 * @param string $destPath   輸出路徑(留空覆蓋原檔)
 * @param int    $maxWidth   最大寬度
 * @param int    $maxHeight  最大高度
 * @param int    $quality    輸出品質 1-100 (JPEG/WebP)
 * @param bool   $upscale    是否允許放大(預設 false)
 * @return bool|array 成功回傳 ['width'=>, 'height'=>, 'path'=>],失敗回傳 false
 */
function createThumbnail(
    string $sourcePath,
    string $destPath = '',
    int $maxWidth = 800,
    int $maxHeight = 600,
    int $quality = 85,
    bool $upscale = false
) {
    // 1. 驗證來源檔案
    if (!file_exists($sourcePath) || !is_readable($sourcePath)) {
        trigger_error("來源檔案不存在或不可讀: $sourcePath", E_USER_WARNING);
        return false;
    }
    
    // 2. 取得圖片資訊
    $imageInfo = @getimagesize($sourcePath);
    if (!$imageInfo) {
        trigger_error("無法識別圖片格式: $sourcePath", E_USER_WARNING);
        return false;
    }
    
    [$srcWidth, $srcHeight, $mimeType] = $imageInfo;
    
    // 3. 計算目標尺寸(等比例)
    $ratio = min($maxWidth / $srcWidth, $maxHeight / $srcHeight);
    if (!$upscale && $ratio >= 1) {
        // 無需縮放,直接複製或回傳原資訊
        if ($destPath && $destPath !== $sourcePath) {
            copy($sourcePath, $destPath);
        }
        return ['width' => $srcWidth, 'height' => $srcHeight, 'path' => $destPath ?: $sourcePath];
    }
    
    $dstWidth  = (int)round($srcWidth * $ratio);
    $dstHeight = (int)round($srcHeight * $ratio);
    
    // 4. 建立來源圖片資源
    $createFunc = match ($mimeType) {
        IMAGETYPE_JPEG   => 'imagecreatefromjpeg',
        IMAGETYPE_PNG    => 'imagecreatefrompng',
        IMAGETYPE_GIF    => 'imagecreatefromgif',
        IMAGETYPE_WEBP   => 'imagecreatefromwebp',
        default          => null,
    };
    
    if (!$createFunc || !function_exists($createFunc)) {
        trigger_error("不支援的圖片格式或 GD 未編譯支援: $mimeType", E_USER_WARNING);
        return false;
    }
    
    $srcImage = $createFunc($sourcePath);
    if (!$srcImage) {
        trigger_error("建立圖片資源失敗: $sourcePath", E_USER_WARNING);
        return false;
    }
    
    // 5. 建立目標真彩色畫布
    $dstImage = imagecreatetruecolor($dstWidth, $dstHeight);
    
    // 6. 處理透明度(PNG/GIF/WebP)
    if (in_array($mimeType, [IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_WEBP], true)) {
        imagealphablending($dstImage, false);
        imagesavealpha($dstImage, true);
        $transparent = imagecolorallocatealpha($dstImage, 255, 255, 255, 127);
        imagefilledrectangle($dstImage, 0, 0, $dstWidth, $dstHeight, $transparent);
    }
    
    // 7. 高品質重採樣
    if (function_exists('imagecopyresampled')) {
        imagecopyresampled($dstImage, $srcImage, 0, 0, 0, 0, $dstWidth, $dstHeight, $srcWidth, $srcHeight);
    } else {
        imagecopyresized($dstImage, $srcImage, 0, 0, 0, 0, $dstWidth, $dstHeight, $srcWidth, $srcHeight);
    }
    
    // 8. 確定輸出路徑與格式
    $outputPath = $destPath ?: $sourcePath;
    $outputMime = $mimeType;
    
    // 若輸出副檔名不同,依副檔名決定格式
    if ($destPath) {
        $ext = strtolower(pathinfo($destPath, PATHINFO_EXTENSION));
        $outputMime = match ($ext) {
            'jpg', 'jpeg' => IMAGETYPE_JPEG,
            'png'         => IMAGETYPE_PNG,
            'gif'         => IMAGETYPE_GIF,
            'webp'        => IMAGETYPE_WEBP,
            default       => $mimeType,
        };
    }
    
    // 9. 輸出圖片
    $success = false;
    switch ($outputMime) {
        case IMAGETYPE_JPEG:
            $success = imagejpeg($dstImage, $outputPath, $quality);
            break;
        case IMAGETYPE_PNG:
            // PNG 品質 0-9 (壓縮等級),轉換 100->0, 0->9
            $pngQuality = (int)round((100 - $quality) / 100 * 9);
            $success = imagepng($dstImage, $outputPath, $pngQuality);
            break;
        case IMAGETYPE_GIF:
            $success = imagegif($dstImage, $outputPath);
            break;
        case IMAGETYPE_WEBP:
            $success = imagewebp($dstImage, $outputPath, $quality);
            break;
    }
    
    // 10. 清理資源
    imagedestroy($srcImage);
    imagedestroy($dstImage);
    
    // 11. 設定檔案權限(可選)
    if ($success && $destPath) {
        @chmod($outputPath, 0644);
    }
    
    return $success 
        ? ['width' => $dstWidth, 'height' => $dstHeight, 'path' => $outputPath]
        : false;
}

使用範例

<?php
require 'thumbnail.php';

// 基本用法:縮圖至 800x600 內,品質 85
$result = createThumbnail('uploads/photo.jpg', 'thumbs/photo.jpg');
if ($result) {
    echo "成功: {$result['width']}x{$result['height']} -> {$result['path']}";
} else {
    echo "失敗";
}

// 進階:指定尺寸、品質、允許放大
createThumbnail(
    'uploads/original.png',
    'thumbs/small.webp',  // 轉檔為 WebP
    300, 300,
    90,
    true  // 允許放大小圖
);

// 批量處理目錄
$files = glob('uploads/*.{jpg,jpeg,png,gif,webp}', GLOB_BRACE);
foreach ($files as $file) {
    $name = basename($file);
    createThumbnail($file, "thumbs/$name", 400, 300, 80);
}

原文程式碼問題分析

// 原文問題程式碼
private function createSmallImg($fileName) {
    list($width,$height,$type,$attr) = getimagesize($fileName);
    $imgOld = imagecreatefromjpeg($fileName);  // 僅支援 JPEG
    $imgObj = imagecreatetruecolor($width-100, $height-100); // 固定減 100px,非等比例
    // ...
    unlink($fileName);  // 危險:直接刪除原檔!
    imagejpeg($imgObj, $fileName, 100);       // 覆蓋原檔,無品質選擇
}

主要缺陷:

  1. 硬編碼 JPEG、固定減少 100px、無比例計算
  2. unlink() 在輸出前刪除原檔,失敗時資料永久遺失
  3. 無錯誤處理、不支援透明度、品質固定 100
  4. 資源釋放順序錯誤(應先銷毀舊資源)

效能優化建議

  • 大量處理:使用 imagick 擴充套件(多執行緒、記憶體效率更好)
  • 快取機制:以「來源檔 mtime + 參數雜湊」為鍵快取縮圖
  • 非同步佇列:上傳後推送任務至 Redis/RabbitMQ,Worker 背景處理
  • WebP 優先:現代瀏覽器全支援,體積較 JPEG 減 25-35%
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved