首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP + Nginx 動態圖片裁剪方案:Imagick 實戰與靜態快取策略

     編輯:PHP 綜合知識

架構設計核心思想

「首次動態生成 + 靜態檔案落地 + Nginx 直接服務靜態檔」,兼具即時性與高效能:

  1. 請求進入 Nginx,優先嘗試讀取靜態檔 (try_files / if (!-f))
  2. 靜態檔不存在 → Rewrite 導向 PHP 裁剪腳本
  3. PHP 裁剪生成圖片 → 儲存至靜態路徑 → 輸出圖片
  4. 後續請求直接命中靜態檔,完全繞過 PHP,效能極致

PHP 裁剪核心腳本 (resize.php)

<?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);

Nginx 配置 (關鍵:靜態檔優先)

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";
    }
}

URL 設計範例

  • 原圖:https://img.example.com/s/www/upload/photo.jpg
  • 300x200 等比縮放:https://img.example.com/s/www/300x200-0/upload/photo.jpg
  • 300x200 智能裁剪:https://img.example.com/s/www/300x200-2/upload/photo.jpg
  • 300x200 填滿裁剪:https://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 安全風險
參數白名單驗證防止路徑遍歷、任意檔案讀取

擴展:支援 WebP/AVIF 現代格式

$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 邊緣節點,圖片載入速度極致。適合電商、內容平台、社交應用等圖片密集型業務。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved