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

PHP 圖片動態裁剪:按需生成任意尺寸縮略圖,Apache Rewrite 實現無感緩存

     編輯:PHP 綜合知識

痛點

門戶類網站同一張圖需在多處展示:列表小圖、詳情大圖、側邊欄方圖……尺寸、比例各異。預先生成所有規格不現實,直接用原圖強制縮放會變形、浪費流量。

解決方案:動態裁剪 + 靜態化緩存

  1. 模板輸出特殊 URL:/data/attachment/forum/abc_200_100.jpg(原圖 abc.jpg + 寬高後綴)。
  2. Apache Rewrite:文件不存在時轉發至處理腳本 images.php。
  3. 腳本按參數裁剪生成文件,輸出並保存。
  4. 下次請求直接命中靜態文件,完全繞過 PHP,性能極高。

核心代碼實現

1. 模板助手函數

<?php
// 返回處理後的圖片 URL
function crop_img($img, $width = 200, $height = 200) {
    $info = parse_url($img);
    // 外鏈直接返回
    if (!empty($info['host']) && $info['host'] !== $_SERVER['HTTP_HOST']) return $img;

    $pos = strrpos($img, '.');
    return substr($img, 0, $pos) . "_${width}_${height}" . substr($img, $pos);
}

// 直接輸出 <img> 標籤
function img($img, $width, $height) {
    $info = parse_url($img);
    if (!empty($info['host']) && $info['host'] !== $_SERVER['HTTP_HOST']) return $img;

    $pos = strrpos($img, '.');
    $new = substr($img, 0, $pos) . "_${width}_${height}" . substr($img, $pos);
    echo "<img src=\"$new\" width=\"$width\" height=\"$height\" />";
}
// Discuz 模板調用:{eval img($pic, 200, 100)}
?>

2. Apache Rewrite 規則 (.htaccess 或 vhost)

<IfModule mod_rewrite.c>
RewriteEngine on
# 僅當文件不存在、目錄不存在時轉發
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^data/attachment/(.*)$ images.php?url=$1 [L]
</IfModule>

3. 處理腳本 images.php(核心裁剪邏輯)

<?php
$url = $_GET['url'];
// 還原原圖路徑:移除 _寬_高 後綴
$src = './data/attachment/' . preg_replace('/_(\d+)_(\d+)/', '', $url);
$dest = './data/attachment/' . $url;

// 文件已存在 → 直接輸出
if (file_exists($dest)) {
    header('Content-Type: image/jpeg');
    readfile($dest);
    exit;
}

// 解析目標尺寸
if (!preg_match('/_(\d+)_(\d+)/', $url, $m)) { default_img(); exit; }
$width = (int)$m[1];
$height = (int)$m[2];

// 執行裁剪
thumb($src, $width, $height, $dest, 'crop', 85);

/**
 * 通用縮略圖生成函數
 * @param string $src 源圖路徑
 * @param int $width 目標寬
 * @param int $height 目標高
 * @param string $dest 保存路徑
 * @param string $mode 'scale'等比縮放(留白) | 'crop'裁剪填滿
 * @param int $quality JPEG質量 1-100
 */
function thumb($src, $width, $height, $dest, $mode = 'scale', $quality = 100) {
    try {
        $info = getimagesize($src);
        if (!$info) throw new Exception('Invalid image');
        list($sw, $sh, $type) = $info;

        // 創建源圖資源
        switch ($type) {
            case IMAGETYPE_JPEG: $source = imagecreatefromjpeg($src); break;
            case IMAGETYPE_GIF:  $source = imagecreatefromgif($src); break;
            case IMAGETYPE_PNG:  $source = imagecreatefrompng($src); break;
            case IMAGETYPE_WBMP: $source = imagecreatefromwbmp($src); break;
            default: throw new Exception('Unsupported type');
        }

        $thumb = imagecreatetruecolor($width, $height);
        // 白色背景(scale 模式留白用)
        imagefill($thumb, 0, 0, imagecolorallocate($thumb, 255, 255, 255));

        // 計算複製區域
        if ($mode === 'scale') {
            // 等比縮放,居中留白
            $ratio = min($width/$sw, $height/$sh);
            $nw = $sw * $ratio; $nh = $sh * $ratio;
            $dx = ($width - $nw) / 2; $dy = ($height - $nh) / 2;
            imagecopyresampled($thumb, $source, $dx, $dy, 0, 0, $nw, $nh, $sw, $sh);
        } else { // crop 模式:填滿目標框,超出部分裁剪
            $srcRatio = $sw / $sh;
            $dstRatio = $width / $height;
            if ($srcRatio > $dstRatio) { // 源圖更寬 → 裁剪左右
                $cropH = $sh;
                $cropW = $sh * $dstRatio;
                $sx = ($sw - $cropW) / 2; $sy = 0;
            } else { // 源圖更高 → 裁剪上下
                $cropW = $sw;
                $cropH = $sw / $dstRatio;
                $sx = 0; $sy = ($sh - $cropH) / 2;
            }
            imagecopyresampled($thumb, $source, 0, 0, $sx, $sy, $width, $height, $cropW, $cropH);
        }

        // 輸出並保存
        header("Content-Type: image/jpeg");
        imagejpeg($thumb, null, $quality);      // 瀏覽器顯示
        imagejpeg($thumb, $dest, $quality);     // 保存緩存文件

        imagedestroy($thumb);
        imagedestroy($source);
    } catch (Exception $e) {
        default_img();
    }
}

function default_img() {
    $img = realpath('media/images/nopic.jpg');
    header('Content-Type: image/jpeg');
    readfile($img);
}
?>

crop vs scale 模式對比

模式行為適用場景
scale等比縮放,短邊留白(白底),完整顯示原圖內容商品圖、頭像、不能裁剪內容的場景
crop填滿目標框,長邊居中裁剪,保證無留白、無變形Banner、列表縮略圖、固定比例容器

優缺點總結

  • ✅ 極其靈活:模板隨意指定尺寸,無需預生成。
  • ✅ 高性能:首次生成後轉為靜態文件,後續請求零 PHP 開銷。
  • ✅ 智能裁剪:crop 模式最大程度保留圖片核心內容。
  • ⚠️ 存儲增長:不同尺寸會產生多份副本,需定期清理或對象存儲處理。
  • ⚠️ 依賴環境:需 Apache mod_rewrite + GD 擴展。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved