門戶類網站同一張圖需在多處展示:列表小圖、詳情大圖、側邊欄方圖……尺寸、比例各異。預先生成所有規格不現實,直接用原圖強制縮放會變形、浪費流量。
/data/attachment/forum/abc_200_100.jpg(原圖 abc.jpg + 寬高後綴)。images.php。<?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)}
?>
<IfModule mod_rewrite.c>
RewriteEngine on
# 僅當文件不存在、目錄不存在時轉發
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^data/attachment/(.*)$ images.php?url=$1 [L]
</IfModule>
<?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);
}
?>
| 模式 | 行為 | 適用場景 |
|---|---|---|
scale | 等比縮放,短邊留白(白底),完整顯示原圖內容 | 商品圖、頭像、不能裁剪內容的場景 |
crop | 填滿目標框,長邊居中裁剪,保證無留白、無變形 | Banner、列表縮略圖、固定比例容器 |