動態圖片裁剪是門戶網站、電商平台的剛需:上傳原圖,按需生成各種規格(縮略圖、列表圖、詳情圖),避免預生成佔用儲存空間。本文提供基於 Imagick + Nginx `try_files` 的高效能方案,含等比縮放、裁剪模式、靜態化落地、瀏覽器快取頭優化。
用戶請求 → Nginx (try_files 靜態檔案) → 命中直接返回
↓ 未命中
Rewrite 到 PHP 裁剪腳本
↓ 生成並保存到靜態路徑
返回圖片 + 快取頭
↓ 後續請求直接命中 Nginx 靜態檔案
<?php
/**
* 高效能動態圖片裁剪
* 依賴:php-imagick 擴展
* URL 範例:/s/www/300x200-2/uploadfile/helloworld.png
* Nginx Rewrite:^([^.]*)/s/(.*)/(\d+)x(\d+)-(\d+)/(.*)$ $1/s/resize.php?site=$2&width=$3&height=$4&mode=$5&path=$6
*/
ini_set('memory_limit', '80M');
// 參數獲取與驗證
$path = trim($_GET['path'] ?? '');
$mode = intval($_GET['mode'] ?? 0);
$site = trim($_GET['site'] ?? '');
$width = intval($_GET['width'] ?? 0);
$height = intval($_GET['height'] ?? 0);
$siteMap = [
'www' => '/mnt/webroot/www/',
'cdn' => '/mnt/webroot/cdn/',
];
if (!isset($siteMap[$site]) || $mode < 0 || $mode > 3 || $width <= 0 || $height <= 0) {
http_response_code(400);
exit('Bad Request');
}
$origFile = $siteMap[$site] . $path;
if (!file_exists($origFile)) {
http_response_code(404);
exit('Not Found');
}
// 輸出路徑:/var/www/html/s/www/300x200-2/uploadfile/helloworld.png
$scriptDir = dirname(__FILE__);
$savePath = "{$scriptDir}/{$site}/{$width}x{$height}-{$mode}/{$path}";
$saveDir = dirname($savePath);
if (!is_dir($saveDir)) {
mkdir($saveDir, 0755, true);
}
// 核心裁剪邏輯
$image = new Imagick($origFile);
[$origW, $origH] = [$image->getImageWidth(), $image->getImageHeight()];
$targetW = $width;
$targetH = $height;
$newW = $targetW;
$newH = $targetH;
if ($mode === 0) {
// 模式 0:等比縮放(含長邊限制)
$newH = $origH * $newW / $origW;
if ($newH > $targetH) {
$newW = $origW * $targetH / $origH;
$newH = $targetH;
}
} elseif ($mode === 2) {
// 模式 2:填充裁剪(類似 CSS object-fit: cover)
$targetRatio = $targetW / $targetH;
$origRatio = $origW / $origH;
if ($targetRatio > $origRatio) {
// 目標更寬 → 裁剪高度
$trimH = $origH - ($origW / $targetRatio);
$image->cropImage($origW, $origH - $trimH, 0, $trimH / 2);
} else {
// 目標更高 → 裁剪寬度
$trimW = $origW - ($origH * $targetRatio);
$image->cropImage($origW - $trimW, $origH, $trimW / 2, 0);
}
}
// 模式 1、3 可擴展:固定寬/高、智慧裁剪等
$image->resizeImage($newW, $newH, Imagick::FILTER_LANCZOS, 1);
$image->writeImage($savePath);
// 輸出圖片 + 快取頭
header('Content-Type: image/jpeg');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: public, max-age=2592000'); // 30 天
echo file_get_contents($savePath);
// 遞迴建立目錄輔助函數
function wpx_mkdir($dir, $mode = 0755) {
return is_dir($dir) || mkdir($dir, $mode, true);
}
server {
listen 80;
server_name img.example.com;
root /var/www/html; # PHP 腳本與生成靜態檔同根目錄
location /s {
# 關鍵:先嘗試靜態檔案,不存在才交給 PHP
try_files $uri $uri/ @imagick;
}
location @imagick {
rewrite ^([^.]*)/s/(.*)/(\d+)x(\d+)-(\d+)/(.*)$ $1/s/resize.php?site=$2&width=$3&height=$4&mode=$5&path=$6 last;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# 靜態資源長期快取
location ~* \.(jpg|jpeg|png|gif|webp|css|js)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Last-Modified,Nginx 靜態檔自動支援。瀏覽器二次請求返回 304 Not Modified,零流量傳輸圖片數據。ini_set('memory_limit', '80M') 防止大圖 OOM。FILTER_LANCZOS(Lanczos 重採樣)品質最佳,速度次之;縮略圖可用 FILTER_TRIANGLE 提速。Content-Type: image/jpeg,簡化快取鍵;原圖 PNG/GIF 透明度場景需保留格式則另行判斷。| Mode | 名稱 | CSS 類比 | 場景 |
|---|---|---|---|
| 0 | 等比縮放 | object-fit: contain | 完整展示、不裁剪 |
| 1 | 固定寬度等比 | width: 300px; height: auto | 列表流、卡片 |
| 2 | 填充裁剪 | object-fit: cover | 封面、Banner、頭像 |
| 3 | 智慧裁剪 | — | 人臉識別、主體區域保留 |
「Nginx 靜態優先 + PHP 動態生成 + 瀏覽器/CDN 多層快取」是動態圖片處理的黃金架構。Imagick 提供專業級圖片處理能力,配合合理的 URL 設計與快取策略,可支撐日均億級圖片請求。核心原則:**生成一次,快取永久,靜態化落地,動態請求極少**。