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

PHP 等比例縮放生成縮圖函數:支援 JPEG/PNG/GIF/WebP 完整實作

     編輯:PHP 綜合知識

核心需求

上傳圖片後,需按指定最大寬高等比縮放生成縮圖,保持原圖比例不變形,支援主流格式,並可控制輸出品質。

完整函數實作

<?php
/**
 * 等比縮放生成縮圖
 * 
 * @param string $srcPath      源圖片路徑
 * @param string $dstPath      目標儲存路徑(不含副檔名,函數會自動加上)
 * @param int    $maxWidth     最大寬度
 * @param int    $maxHeight    最大高度
 * @param int    $quality      輸出品質 1-100 (JPEG/WebP) 或壓縮等級 0-9 (PNG)
 * @return string|false        成功回傳生成的檔案名,失敗回傳 false
 */
function createThumbnail(string $srcPath, string $dstPath, int $maxWidth = 200, int $maxHeight = 200, int $quality = 85): string|false {
    // 1. 取得原圖資訊
    $imageInfo = @getimagesize($srcPath);
    if (!$imageInfo) {
        trigger_error("無法讀取圖片:{$srcPath}", E_USER_WARNING);
        return false;
    }
    
    [$srcWidth, $srcHeight, $type] = $imageInfo;
    
    // 2. 若原圖已小於限制,直接複製或回傳 false
    if ($srcWidth <= $maxWidth && $srcHeight <= $maxHeight) {
        // 可選:直接複製原檔
        // copy($srcPath, $dstPath . '.' . image_type_to_extension($type, false));
        return false;
    }
    
    // 3. 計算縮放比例(取較小者,確保長寬皆不超過限制)
    $scale = min($maxWidth / $srcWidth, $maxHeight / $srcHeight);
    $dstWidth  = (int)round($srcWidth * $scale);
    $dstHeight = (int)round($srcHeight * $scale);
    
    // 4. 建立真彩色畫布
    $dstImage = imagecreatetruecolor($dstWidth, $dstHeight);
    
    // 5. 針對透明度格式 (PNG/GIF/WebP) 保持透明通道
    if (in_array($type, [IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_WEBP])) {
        imagealphablending($dstImage, false);
        imagesavealpha($dstImage, true);
        $transparent = imagecolorallocatealpha($dstImage, 255, 255, 255, 127);
        imagefilledrectangle($dstImage, 0, 0, $dstWidth, $dstHeight, $transparent);
    }
    
    // 6. 根據類型建立源圖資源
    $srcImage = match($type) {
        IMAGETYPE_GIF  => imagecreatefromgif($srcPath),
        IMAGETYPE_JPEG => imagecreatefromjpeg($srcPath),
        IMAGETYPE_PNG  => imagecreatefrompng($srcPath),
        IMAGETYPE_WEBP => imagecreatefromwebp($srcPath),
        default        => null
    };
    
    if (!$srcImage) {
        trigger_error("不支援的圖片格式:{$type}", E_USER_WARNING);
        imagedestroy($dstImage);
        return false;
    }
    
    // 7. 高品質重採樣縮放
    imagecopyresampled($dstImage, $srcImage, 0, 0, 0, 0, $dstWidth, $dstHeight, $srcWidth, $srcHeight);
    
    // 8. 移除副檔名(若傳入含副檔名)
    $dstPath = preg_replace('/\.(gif|jpe?g|png|webp)$/i', '', $dstPath);
    
    // 9. 根據原格式輸出(或可加參數指定輸出格式)
    $success = false;
    $ext = '';
    switch ($type) {
        case IMAGETYPE_GIF:
            $ext = '.gif';
            $success = imagegif($dstImage, $dstPath . $ext);
            break;
        case IMAGETYPE_JPEG:
            $ext = '.jpg';
            $success = imagejpeg($dstImage, $dstPath . $ext, $quality);
            break;
        case IMAGETYPE_PNG:
            $ext = '.png';
            // PNG quality: 0-9 壓縮等級,對應 0-100 映射
            $pngQuality = (int)round($quality / 100 * 9);
            $success = imagepng($dstImage, $dstPath . $ext, $pngQuality);
            break;
        case IMAGETYPE_WEBP:
            $ext = '.webp';
            $success = imagewebp($dstImage, $dstPath . $ext, $quality);
            break;
    }
    
    // 10. 清理資源
    imagedestroy($srcImage);
    imagedestroy($dstImage);
    
    return $success ? basename($dstPath . $ext) : false;
}

使用範例

<?php
// 基本用法
$result = createThumbnail(
    'uploads/photo.jpg',    // 源檔
    'thumbs/photo_thumb',   // 目標路徑(不含副檔名)
    300, 300,               // 最大 300x300
    85                      // JPEG 品質 85
);

if ($result) {
    echo "縮圖生成成功:{$result}"; // 輸出:photo_thumb.jpg
} else {
    echo "縮圖生成失敗或原圖已小於限制";
}

// 批次處理上傳檔案
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['avatar'])) {
    $file = $_FILES['avatar'];
    if ($file['error'] === UPLOAD_ERR_OK) {
        $ext = pathinfo($file['name'], PATHINFO_EXTENSION);
        $newName = 'avatar_' . uniqid() . '.' . $ext;
        $uploadPath = 'uploads/' . $newName;
        $thumbPath = 'thumbs/avatar_' . uniqid();
        
        if (move_uploaded_file($file['tmp_name'], $uploadPath)) {
            $thumb = createThumbnail($uploadPath, $thumbPath, 150, 150, 90);
            echo "上傳成功,縮圖:{$thumb}";
        }
    }
}
?>

進階擴展:支援裁切模式 (Crop/Fill)

<?php
/**
 * 進階縮圖:支援 fit(等比縮放)、fill(裁切填滿)、stretch(拉伸)
 */
function createThumbnailAdvanced(
    string $srcPath, 
    string $dstPath, 
    int $width, 
    int $height, 
    string $mode = 'fit',      // fit | fill | stretch
    int $quality = 85,
    array $bgColor = [255,255,255]  // fit 模式下的填充色
): string|false {
    $info = @getimagesize($srcPath);
    if (!$info) return false;
    [$sw, $sh, $type] = $info;
    
    $src = match($type) {
        IMAGETYPE_GIF  => imagecreatefromgif($srcPath),
        IMAGETYPE_JPEG => imagecreatefromjpeg($srcPath),
        IMAGETYPE_PNG  => imagecreatefrompng($srcPath),
        IMAGETYPE_WEBP => imagecreatefromwebp($srcPath),
        default => null
    };
    if (!$src) return false;
    
    $dst = imagecreatetruecolor($width, $height);
    
    // 透明度支援
    if (in_array($type, [IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_WEBP])) {
        imagealphablending($dst, false);
        imagesavealpha($dst, true);
    } else {
        // 填充背景色
        $bg = imagecolorallocate($dst, $bgColor[0], $bgColor[1], $bgColor[2]);
        imagefill($dst, 0, 0, $bg);
    }
    
    switch ($mode) {
        case 'fit': // 等比縮放進框內
            $scale = min($width/$sw, $height/$sh);
            $dw = (int)round($sw * $scale);
            $dh = (int)round($sh * $scale);
            $dx = (int)(($width - $dw) / 2);
            $dy = (int)(($height - $dh) / 2);
            imagecopyresampled($dst, $src, $dx, $dy, 0, 0, $dw, $dh, $sw, $sh);
            break;
            
        case 'fill': // 等比縮放並裁切填滿
            $scale = max($width/$sw, $height/$sh);
            $dw = (int)round($sw * $scale);
            $dh = (int)round($sh * $scale);
            $dx = (int)(($width - $dw) / 2);
            $dy = (int)(($height - $dh) / 2);
            imagecopyresampled($dst, $src, $dx, $dy, 0, 0, $dw, $dh, $sw, $sh);
            break;
            
        case 'stretch': // 非等比拉伸
            imagecopyresampled($dst, $src, 0, 0, 0, 0, $width, $height, $sw, $sh);
            break;
    }
    
    // 輸出(同基本版)
    $dstPath = preg_replace('/\.(gif|jpe?g|png|webp)$/i', '', $dstPath);
    $extMap = [
        IMAGETYPE_GIF => '.gif', IMAGETYPE_JPEG => '.jpg',
        IMAGETYPE_PNG => '.png', IMAGETYPE_WEBP => '.webp'
    ];
    $ext = $extMap[$type] ?? '.jpg';
    
    $success = match($type) {
        IMAGETYPE_GIF  => imagegif($dst, $dstPath . $ext),
        IMAGETYPE_JPEG => imagejpeg($dst, $dstPath . $ext, $quality),
        IMAGETYPE_PNG  => imagepng($dst, $dstPath . $ext, (int)round($quality/100*9)),
        IMAGETYPE_WEBP => imagewebp($dst, $dstPath . $ext, $quality),
        default => false
    };
    
    imagedestroy($src);
    imagedestroy($dst);
    
    return $success ? basename($dstPath . $ext) : false;
}

GD vs Imagick 選擇建議

特性GDImagick
安裝便利性PHP 內建,無需額外安裝需安裝 ImageMagick + 擴展
格式支援JPEG/PNG/GIF/WebP/BMP185+ 格式含 PDF/TIFF/HEIC/RAW
GIF 動畫不支援(僅第一幀)完整支援多幀處理
記憶體效率較高(全載入記憶體)可串流處理、資源限制較彈性
效能簡單操作較快複雜操作、大圖優勢明顯
文字渲染基礎 FreeType專業排版、OpenType、路徑文字

常見問題排查

  • 輸出檔案 0 bytes:檢查目錄寫入權限、imagejpeg() 等回傳值
  • 透明背景變黑:確認 imagealphablending(false) + imagesavealpha(true)
  • 記憶體不足:大圖處理前 ini_set('memory_limit', '256M')
  • WebP 不支援:需 PHP 5.5+ 且編譯時 --with-webp

這個縮圖函數輕量、無依賴、支援主流格式與透明度,適合中小型專案快速整合。大型高併發場景建議改用 Imagick 或離線異步處理隊列。

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