<form enctype="multipart/form-data" action="upload.php" method="post">
<input name="upfile" type="file" accept="image/*"><br>
<input type="submit" value="上傳並生成縮圖">
</form>
<?php
// 設定
$uploadDir = 'uploads/';
$thumbDir = 'thumbs/';
$maxWidth = 300;
$maxHeight = 300;
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true);
if (!is_dir($thumbDir)) mkdir($thumbDir, 0755, true);
// 檢查上傳
if (!isset($_FILES['upfile']) || $_FILES['upfile']['error'] !== UPLOAD_ERR_OK) {
die("上傳失敗: " . ($_FILES['upfile']['error'] ?? '無檔案'));
}
$tmpName = $_FILES['upfile']['tmp_name'];
$origName = basename($_FILES['upfile']['name']);
$mime = mime_content_type($tmpName);
if (!in_array($mime, $allowedTypes)) {
die("不支援的檔案類型: $mime");
}
// 產生安全檔名
$ext = ['image/jpeg'=>'jpg', 'image/png'=>'png', 'image/gif'=>'gif', 'image/webp'=>'webp'][$mime];
$safeName = bin2hex(random_bytes(8)) . ".$ext";
$destPath = $uploadDir . $safeName;
$thumbPath = $thumbDir . 'thumb_' . $safeName;
// 移動上傳檔
if (!move_uploaded_file($tmpName, $destPath)) {
die("檔案儲存失敗");
}
// === 核心縮圖邏輯 ===
list($srcW, $srcH, $srcType) = getimagesize($destPath);
// 計算目標尺寸 (維持比例)
$ratio = $srcW / $srcH;
if ($maxWidth / $maxHeight > $ratio) {
$dstW = (int)($maxHeight * $ratio);
$dstH = $maxHeight;
} else {
$dstW = $maxWidth;
$dstH = (int)($maxWidth / $ratio);
}
// 建立目標畫布
$dstImg = imagecreatetruecolor($dstW, $dstH);
// 載入來源圖片
switch ($srcType) {
case IMAGETYPE_JPEG: $srcImg = imagecreatefromjpeg($destPath); break;
case IMAGETYPE_PNG: $srcImg = imagecreatefrompng($destPath); break;
case IMAGETYPE_GIF: $srcImg = imagecreatefromgif($destPath); break;
case IMAGETYPE_WEBP: $srcImg = imagecreatefromwebp($destPath); break;
default: die("不支援的圖片格式");
}
// 保持透明度 (PNG/GIF/WEBP)
if (in_array($srcType, [IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_WEBP])) {
imagealphablending($dstImg, false);
imagesavealpha($dstImg, true);
}
// 高品質重採樣
imagecopyresampled($dstImg, $srcImg, 0, 0, 0, 0, $dstW, $dstH, $srcW, $srcH);
// 儲存縮圖 (依原格式)
switch ($srcType) {
case IMAGETYPE_JPEG: imagejpeg($dstImg, $thumbPath, 85); break;
case IMAGETYPE_PNG: imagepng($dstImg, $thumbPath, 9); break;
case IMAGETYPE_GIF: imagegif($dstImg, $thumbPath); break;
case IMAGETYPE_WEBP: imagewebp($dstImg, $thumbPath, 85); break;
}
// 釋放資源
imagedestroy($srcImg);
imagedestroy($dstImg);
// 輸出結果
echo "上傳成功!
";
echo "<img src='$destPath' alt='原圖' style='max-width:300px'> ";
echo "<img src='$thumbPath' alt='縮圖' style='max-width:300px'>";
?>
function generateThumbnails($srcPath, $destDir, $sizes = []) {
// $sizes = ['small'=>[150,150], 'medium'=>[300,300], 'large'=>[800,600]]
list($w, $h, $type) = getimagesize($srcPath);
$src = loadImageByType($srcPath, $type);
foreach ($sizes as $name => [$maxW, $maxH]) {
$ratio = $w / $h;
if ($maxW / $maxH > $ratio) {
$dstW = (int)($maxH * $ratio);
$dstH = $maxH;
} else {
$dstW = $maxW;
$dstH = (int)($maxW / $ratio);
}
$dst = imagecreatetruecolor($dstW, $dstH);
if (in_array($type, [IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_WEBP])) {
imagealphablending($dst, false);
imagesavealpha($dst, true);
}
imagecopyresampled($dst, $src, 0, 0, 0, 0, $dstW, $dstH, $w, $h);
$ext = getExtByType($type);
$outPath = "$destDir/{$name}_$dstW" . "x$dstH.$ext";
saveImageByType($dst, $outPath, $type);
imagedestroy($dst);
}
imagedestroy($src);
return true;
}
| 優化點 | 方案 |
|---|---|
| 大量上傳 | 異步佇列處理,立即回應用戶 |
| 重複尺寸請求 | 快取機制:檢查縮圖是否存在再生成 |
| 記憶體峰值 | 分塊處理、及時 imagedestroy |
| CPU 密集 | 考慮 ImageMagick (imagick 擴展) 效能更佳 |
// Imagick 版本 (更簡潔、效能更好、格式支援更多) $imagick = new Imagick($srcPath); $imagick->thumbnailImage(300, 300, true, true); // true=維持比例 $imagick->writeImage($thumbPath); $imagick->destroy();
GD 庫縮圖是 PHP 內建零依賴方案,掌握 imagecopyresampled 核心函式與透明度保持技巧,即可應對大多數縮圖需求。