驗證碼生成、統計圖表繪製、使用者上傳頭像裁切、產品圖加浮水印、縮圖自動生成——這些 Web 開發高頻需求,核心都是**對現有圖片資源進行讀取、操作、輸出**。PHP GD 庫提供了完整的圖片處理函數族群,本文將系統梳理從「載入現有圖片」到「輸出儲存」的標準化流程與最佳實踐。
| 格式 | 載入函數 | 輸出函數 | MIME 類型 |
|---|---|---|---|
| JPEG | imagecreatefromjpeg() | imagejpeg() | image/jpeg |
| PNG | imagecreatefrompng() | imagepng() | image/png |
| GIF | imagecreatefromgif() | imagegif() | image/gif |
| WBMP | imagecreatefromwbmp() | imagewbmp() | image/vnd.wap.wbmp |
| WebP | imagecreatefromwebp() (PHP 5.5+) | imagewebp() | image/webp |
所有載入函數成功回傳 圖片資源識別符,失敗回傳 false 並觸發 E_WARNING。
利用 getimagesize() 取得圖片資訊,動態選擇載入函數:
<?php
function loadImage($filename) {
if (!file_exists($filename)) {
throw new Exception("檔案不存在:$filename");
}
$info = getimagesize($filename);
if ($info === false) {
throw new Exception("無效圖片檔案:$filename");
}
// $info[2] 對應 IMAGETYPE_* 常數
$type = $info[2];
$mime = $info['mime'];
$loadFunc = [
IMAGETYPE_GIF => 'imagecreatefromgif',
IMAGETYPE_JPEG => 'imagecreatefromjpeg',
IMAGETYPE_PNG => 'imagecreatefrompng',
IMAGETYPE_WEBP => 'imagecreatefromwebp',
IMAGETYPE_BMP => 'imagecreatefrombmp', // PHP 7.2+
];
if (!isset($loadFunc[$type])) {
throw new Exception("不支援的圖片格式:$mime");
}
$func = $loadFunc[$type];
$resource = $func($filename);
if ($resource === false) {
throw new Exception("圖片載入失敗:$filename");
}
return [
'resource' => $resource,
'width' => $info[0],
'height' => $info[1],
'type' => $type,
'mime' => $mime,
];
}
// 使用範例
try {
$img = loadImage('photo.jpg');
// $img['resource'] 即為圖片資源
// $img['width'], $img['height'] 可直接使用
} catch (Exception $e) {
echo "錯誤:" . $e->getMessage();
}
?>
<?php
$info = getimagesize('image/brophp.jpg');
/*
$info[0] => 寬度 (像素)
$info[1] => 高度 (像素)
$info[2] => 類型常數 (1=GIF, 2=JPG, 3=PNG, 4=SWF, ...)
$info[3] => 字串 "width=xxx height=yyy" 可直接用於 <img> 標籤
$info['bits'] => 色彩位深
$info['channels'] => 通道數 (3=RGB, 4=RGBA)
$info['mime'] => MIME 類型
*/
list($width, $height, $type, $attr) = $info;
echo "<img src='image/brophp.jpg' $attr>";
// 輸出:<img src='image/brophp.jpg' width=800 height=600>
?>
實務上常見需求:開啟何種格式,儲存何種格式。以下模式完美解決:
<?php
function saveImage($resource, $filename, $type = null, $quality = 90) {
// 若未指定類型,從副檔名推斷
if ($type === null) {
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$typeMap = [
'jpg' => IMAGETYPE_JPEG,
'jpeg' => IMAGETYPE_JPEG,
'png' => IMAGETYPE_PNG,
'gif' => IMAGETYPE_GIF,
'webp' => IMAGETYPE_WEBP,
'bmp' => IMAGETYPE_BMP,
];
$type = $typeMap[$ext] ?? IMAGETYPE_JPEG;
}
$saveFunc = [
IMAGETYPE_JPEG => function($res, $file) use ($quality) {
return imagejpeg($res, $file, $quality);
},
IMAGETYPE_PNG => function($res, $file) use ($quality) {
// PNG 品質 0-9 (壓縮等級),需轉換
$pngQuality = min(9, max(0, (100 - $quality) / 10));
return imagepng($res, $file, (int)$pngQuality);
},
IMAGETYPE_GIF => function($res, $file) {
return imagegif($res, $file);
},
IMAGETYPE_WEBP => function($res, $file) use ($quality) {
return imagewebp($res, $file, $quality);
},
];
if (!isset($saveFunc[$type])) {
throw new Exception("不支援的儲存格式類型:$type");
}
return $saveFunc[$type]($resource, $filename);
}
// 實戰範例:開啟任意格式 → 加文字水印 → 存回原格式
function addWatermark($srcFile, $text, $dstFile = null) {
$img = loadImage($srcFile);
$res = $img['resource'];
$w = $img['width'];
$h = $img['height'];
// 分配紅色
$color = imagecolorallocate($res, 255, 0, 0);
// 字體大小 5 (內建)
$font = 5;
$tw = imagefontwidth($font) * strlen($text);
$th = imagefontheight($font);
// 置中座標
$x = ($w - $tw) / 2;
$y = ($h - $th) / 2;
imagestring($res, $font, $x, $y, $text, $color);
// 儲存
$target = $dstFile ?? $srcFile;
saveImage($res, $target, $img['type']);
imagedestroy($res);
return true;
}
// 批次處理
addWatermark("brophp.gif", "GIF");
addWatermark("brophp.jpg", "JPEG");
addWatermark("brophp.png", "PNG");
?>
<?php
function resizeImage($srcFile, $maxWidth, $maxHeight, $dstFile) {
$img = loadImage($srcFile);
$srcW = $img['width'];
$srcH = $img['height'];
// 計算縮放比例
$ratio = min($maxWidth / $srcW, $maxHeight / $srcH);
if ($ratio >= 1) { // 無需縮小
copy($srcFile, $dstFile);
return;
}
$dstW = (int)($srcW * $ratio);
$dstH = (int)($srcH * $ratio);
// 建立真彩色目標畫布
$dst = imagecreatetruecolor($dstW, $dstH);
// 保持透明度 (PNG/GIF)
if ($img['type'] == IMAGETYPE_PNG || $img['type'] == IMAGETYPE_GIF) {
imagealphablending($dst, false);
imagesavealpha($dst, true);
$transparent = imagecolorallocatealpha($dst, 255, 255, 255, 127);
imagefilledrectangle($dst, 0, 0, $dstW, $dstH, $transparent);
}
// 重採樣縮放 (高品質)
imagecopyresampled($dst, $img['resource'], 0, 0, 0, 0, $dstW, $dstH, $srcW, $srcH);
saveImage($dst, $dstFile, $img['type']);
imagedestroy($dst);
imagedestroy($img['resource']);
}
?>
<?php
function cropImage($srcFile, $x, $y, $width, $height, $dstFile) {
$img = loadImage($srcFile);
$dst = imagecreatetruecolor($width, $height);
// 透明度處理同縮放
if (in_array($img['type'], [IMAGETYPE_PNG, IMAGETYPE_GIF])) {
imagealphablending($dst, false);
imagesavealpha($dst, true);
}
imagecopy($dst, $img['resource'], 0, 0, $x, $y, $width, $height);
saveImage($dst, $dstFile, $img['type']);
imagedestroy($dst);
imagedestroy($img['resource']);
}
?>
<?php
function rotateImage($srcFile, $angle, $dstFile) {
$img = loadImage($srcFile);
// imagerotate 會自動擴展畫布,背景色需指定
$bgColor = imagecolorallocatealpha($img['resource'], 255, 255, 255, 127);
$rotated = imagerotate($img['resource'], $angle, $bgColor);
// 保持透明
if (in_array($img['type'], [IMAGETYPE_PNG, IMAGETYPE_GIF])) {
imagealphablending($rotated, false);
imagesavealpha($rotated, true);
}
saveImage($rotated, $dstFile, $img['type']);
imagedestroy($rotated);
imagedestroy($img['resource']);
}
function flipImage($srcFile, $mode, $dstFile) {
// $mode: 'horizontal' (水平鏡像) 或 'vertical' (垂直翻轉)
$img = loadImage($srcFile);
$w = $img['width'];
$h = $img['height'];
$dst = imagecreatetruecolor($w, $h);
if ($mode === 'horizontal') {
// 從右往左複製每一列
for ($x = 0; $x < $w; $x++) {
imagecopy($dst, $img['resource'], $x, 0, $w - 1 - $x, 0, 1, $h);
}
} else {
// 從下往上複製每一行
for ($y = 0; $y < $h; $y++) {
imagecopy($dst, $img['resource'], 0, $y, 0, $h - 1 - $y, $w, 1);
}
}
saveImage($dst, $dstFile, $img['type']);
imagedestroy($dst);
imagedestroy($img['resource']);
}
?>
imagecreatefrom* 都必須配對 imagedestroy(),否則記憶體洩漏嚴重。@ 抑制警告,搭配 error_get_last() 捕捉。| 現象 | 可能原因 | 解決方案 |
|---|---|---|
| 輸出圖片空白/破損 | 未送出正確 Header / 輸出前有空白字元 | 確保 header() 在任何輸出前;檢查檔案結尾無 ?> 後空白 |
| PNG/GIF 透明背景變黑 | 未啟用 Alpha blending / savealpha | 目標畫布設定 imagealphablending(false); imagesavealpha(true); |
| 中文文字顯示方塊 | 字體不支援中文 / 編碼非 UTF-8 | 使用支援中文的 .ttf/.ttc 字體;確保腳本 UTF-8 編碼 |
| JPEG 品質差/檔案過大 | 品質參數設定不當 | imagejpeg($res, $file, 85) 85-90 為平衡點 |
| 記憶體耗盡 | 處理超大圖片 / 未釋放資源 | 增加 memory_limit;及時 imagedestroy();分塊處理 |
PHP GD 圖片處理的核心在於:**正確載入 → 靈活操作 → 精準輸出 → 必須釋放**。掌握 getimagesize() 動態偵測格式、建立通用載入/儲存封裝函數、妥善處理透明度與記憶體管理,即能構建健壯、可擴充的圖片處理服務。無論是簡單的水印加文字、縮圖生成,還是複雜的圖片合成、濾鏡效果,皆遵循此一核心流程。