<?php
/**
* 等比例縮放圖片
* @param string $srcPath 來源檔案路徑
* @param int $maxW 最大寬度
* @param int $maxH 最大高度
* @param string $destPath 輸出路徑(預設覆蓋來源)
* @param int $quality JPEG 品質 1-100
* @return bool
*/
function resizeProportional(string $srcPath, int $maxW, int $maxH, string $destPath = '', int $quality = 90): bool
{
if (!file_exists($srcPath)) return false;
$destPath = $destPath ?: $srcPath;
[$w, $h, $type] = getimagesize($srcPath);
// 無需縮放
if ($w <= $maxW && $h <= $maxH) {
return $srcPath !== $destPath ? copy($srcPath, $destPath) : true;
}
// 計算縮放比例
$ratio = min($maxW / $w, $maxH / $h);
$newW = (int)($w * $ratio);
$newH = (int)($h * $ratio);
// 建立來源圖片資源
$src = match ($type) {
IMAGETYPE_JPEG => imagecreatefromjpeg($srcPath),
IMAGETYPE_PNG => imagecreatefrompng($srcPath),
IMAGETYPE_GIF => imagecreatefromgif($srcPath),
default => return false,
};
// 建立目標畫布(真彩色)
$dst = imagecreatetruecolor($newW, $newH);
// PNG/GIF 保持透明度
if ($type === IMAGETYPE_PNG || $type === IMAGETYPE_GIF) {
imagealphablending($dst, false);
imagesavealpha($dst, true);
$transparent = imagecolorallocatealpha($dst, 255, 255, 255, 127);
imagefilledrectangle($dst, 0, 0, $newW, $newH, $transparent);
}
// 高品質重採樣
imagecopyresampled($dst, $src, 0, 0, 0, 0, $newW, $newH, $w, $h);
// 輸出
$result = match ($type) {
IMAGETYPE_JPEG => imagejpeg($dst, $destPath, $quality),
IMAGETYPE_PNG => imagepng($dst, $destPath),
IMAGETYPE_GIF => imagegif($dst, $destPath),
default => false,
};
imagedestroy($src);
imagedestroy($dst);
return $result;
}
<?php
// 產生最大 300x300 縮圖
resizeProportional('uploads/photo.jpg', 300, 300, 'uploads/thumb_photo.jpg');
// 批次處理目錄
foreach (glob('uploads/*.{jpg,png,gif}', GLOB_BRACE) as $file) {
$thumb = 'thumbs/' . basename($file);
resizeProportional($file, 200, 200, $thumb);
}
| 細節 | 處理方式 |
|---|---|
| 長寬比保持 | min($maxW/$w, $maxH/$h) 取較小比例,確保不超出任一邊界 |
| 不放大小圖 | 先判斷 $w <= $maxW && $h <= $maxH 直接複製 |
| PNG/GIF 透明度 | 關閉混色、啟用 alpha 儲存、建立透明底 |
| 畫質 | 使用 imagecopyresampled 而非 imagecopyresized |
| 記憶體 | 處理完立即 imagedestroy() 釋放 GD 資源 |
若需「填滿區域並裁切多餘」(如封面圖),改用裁切邏輯:
function cropCover($srcPath, $targetW, $targetH, $destPath) {
[$w, $h, $type] = getimagesize($srcPath);
$srcRatio = $w / $h;
$dstRatio = $targetW / $targetH;
if ($srcRatio > $dstRatio) {
// 來源較寬 → 裁切左右
$cropH = $h;
$cropW = $h * $dstRatio;
$x = ($w - $cropW) / 2;
$y = 0;
} else {
// 來源較高 → 裁切上下
$cropW = $w;
$cropH = $w / $dstRatio;
$x = 0;
$y = ($h - $cropH) / 2;
}
// 後續同 resizeProportional,但來源區域為 ($x, $y, $cropW, $cropH)
}
memory_limit 或分塊處理。imagecopyresampled,且 JPEG 品質 ≥ 80。imagesavealpha(true) 並建立透明底。這個函式已在多個專案的上傳流程中穩定運行,支援三大常見格式,程式碼簡潔易維護。