「首次動態生成 + 靜態檔案落地 + Nginx 直接服務靜態檔」,兼具即時性與高效能:
try_files / if (!-f))<?php
ini_set('memory_limit', '256M'); // 大圖處理需較大記憶體
// 參數獲取與驗證
$path = trim($_GET['path'] ?? '');
$mode = intval($_GET['mode'] ?? 0);
$site = trim($_GET['site'] ?? '');
$width = intval($_GET['width'] ?? 0);
$height = intval($_GET['height'] ?? 0);
// 站點根目錄映射 (安全:防止目錄遍歷)
$siteRoots = [
'www' => '/mnt/webroot/www/',
'img' => '/mnt/webroot/cdn/',
];
if (!isset($siteRoots[$site]) || $width <= 0 || $height <= 0 || $mode < 0 || $mode > 3) {
http_response_code(400);
exit('Invalid parameters');
}
$origFile = $siteRoots[$site] . $path;
if (!file_exists($origFile)) {
http_response_code(404);
exit('Source image not found');
}
// 輸出路徑:/var/www/cache/www/300x200-2/upload/photo.jpg
$saveDir = __DIR__ . "/cache/{$site}/{$width}x{$height}-{$mode}";
$savePath = $saveDir . '/' . $path;
if (!is_dir($saveDir)) {
mkdir($saveDir, 0755, true); // 遞迴建立目錄
}
// 核心裁剪邏輯 (Imagick)
$image = new Imagick($origFile);
$origW = $image->getImageWidth();
$origH = $image->getImageHeight();
$targetW = $width;
$targetH = $height;
$newW = $targetW;
$newH = $targetH;
switch ($mode) {
case 0: // 等比縮放 (fit inside)
$ratio = min($targetW / $origW, $targetH / $origH);
$newW = (int)($origW * $ratio);
$newH = (int)($origH * $ratio);
break;
case 1: // 等比縮放填滿 (cover) + 居中裁剪
$ratio = max($targetW / $origW, $targetH / $origH);
$newW = (int)($origW * $ratio);
$newH = (int)($origH * $ratio);
// 先縮放再裁剪
$image->resizeImage($newW, $newH, Imagick::FILTER_LANCZOS, 1);
$x = ($newW - $targetW) / 2;
$y = ($newH - $targetH) / 2;
$image->cropImage($targetW, $targetH, $x, $y);
$newW = $targetW;
$newH = $targetH;
break;
case 2: // 智能裁剪 (原文算法:按目標比例裁剪原圖再縮放)
$targetRatio = $targetW / $targetH;
$origRatio = $origW / $origH;
if ($targetRatio > $origRatio) {
// 目標較寬:裁剪高度
$cropH = $origW / $targetRatio;
$image->cropImage($origW, $cropH, 0, ($origH - $cropH) / 2);
} else {
// 目標較高:裁剪寬度
$cropW = $origH * $targetRatio;
$image->cropImage($cropW, $origH, ($origW - $cropW) / 2, 0);
}
break;
case 3: // 不縮放,僅裁剪左上角
$image->cropImage(min($targetW, $origW), min($targetH, $origH), 0, 0);
$newW = min($targetW, $origW);
$newH = min($targetH, $origH);
break;
}
if ($mode !== 1 && $mode !== 3) { // mode 1/3 已在 case 內處理
$image->resizeImage($newW, $newH, Imagick::FILTER_LANCZOS, 1);
}
// 輸出與快取
$image->writeImage($savePath);
$image->destroy(); // 釋放記憶體
// 瀏覽器快取標頭 (關鍵:支援 304 Not Modified)
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 天
readfile($savePath);
server {
listen 80;
server_name img.example.com;
root /mnt/webroot; # 靜態檔根目錄
location /s/ {
# 核心:檔案存在直接服務,不存在才交給 PHP
try_files $uri $uri/ @resize;
# 或舊版寫法
# if (!-f $request_filename) {
# rewrite ^/s/(.*)/(\d+)x(\d+)-(\d)/(.*)$ /s/resize.php?site=$1&width=$2&height=$3&mode=$4&path=$5 last;
# }
}
location @resize {
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root/s/resize.php;
include fastcgi_params;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
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";
}
}
https://img.example.com/s/www/upload/photo.jpghttps://img.example.com/s/www/300x200-0/upload/photo.jpghttps://img.example.com/s/www/300x200-2/upload/photo.jpghttps://img.example.com/s/www/300x200-1/upload/photo.jpg| 優化點 | 說明 |
|---|---|
| Imagick FILTER_LANCZOS | 高品質重採樣濾鏡,縮放清銳度最佳 |
| 記憶體限制 | ini_set('memory_limit', '256M') 防大圖 OOM |
| 銷毀物件 | $image->destroy() 及時釋放 Imagick 記憶體 |
| Last-Modified + ETag | 支援瀏覽器 304 快取,二次請求零流量 |
| 目錄權限 0755 | 避免 0777 安全風險 |
| 參數白名單驗證 | 防止路徑遍歷、任意檔案讀取 |
$image->setImageFormat('webp');
header('Content-Type: image/webp');
// 或根據 Accept 標頭自動選擇
if (strpos($_SERVER['HTTP_ACCEPT'] ?? '', 'image/webp') !== false) {
$image->setImageFormat('webp');
header('Content-Type: image/webp');
}
此方案實現 「生成一次,永久靜態化」,首次請求經 PHP 裁剪 (約 50-200ms),後續請求純 Nginx 靜態服務 (<1ms)。配合 CDN 邊緣節點,圖片載入速度極致。適合電商、內容平台、社交應用等圖片密集型業務。