上傳圖片後,需按指定最大寬高等比縮放生成縮圖,保持原圖比例不變形,支援主流格式,並可控制輸出品質。
<?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}";
}
}
}
?>
<?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 | Imagick |
|---|---|---|
| 安裝便利性 | PHP 內建,無需額外安裝 | 需安裝 ImageMagick + 擴展 |
| 格式支援 | JPEG/PNG/GIF/WebP/BMP | 185+ 格式含 PDF/TIFF/HEIC/RAW |
| GIF 動畫 | 不支援(僅第一幀) | 完整支援多幀處理 |
| 記憶體效率 | 較高(全載入記憶體) | 可串流處理、資源限制較彈性 |
| 效能 | 簡單操作較快 | 複雜操作、大圖優勢明顯 |
| 文字渲染 | 基礎 FreeType | 專業排版、OpenType、路徑文字 |
imagejpeg() 等回傳值imagealphablending(false) + imagesavealpha(true)ini_set('memory_limit', '256M')--with-webp這個縮圖函數輕量、無依賴、支援主流格式與透明度,適合中小型專案快速整合。大型高併發場景建議改用 Imagick 或離線異步處理隊列。