在 Web 開發中,縮略圖的生成時機通常有兩種策略:上傳時生成與訪問時生成。兩者各有優劣,選擇哪種取決於具體業務場景。
優點: 圖片上傳時即預先生成所需尺寸,讀取時無需判斷邏輯,減少 CPU 運算開銷,響應速度快。
缺點: 當縮略圖尺寸需求變更或新增尺寸時,必須重新遍歷所有原圖批量生成,維護成本高,佔用存儲空間大。
優點:
缺點: 高併發訪問同一張尚未生成的縮略圖時,會觸發多進程重複生成,導致服務器資源耗盡、響應超時甚至宕機。
綜合來看,訪問時生成策略在靈活性和空間效率上更優,關鍵在於解決高併發下的重複生成問題。
核心思路:利用文件系統的原子性操作,在生成縮略圖前創建唯一標記文件,其他請求檢測到標記存在則進入等待,直到生成完成並刪除標記。
/tmp/ 目錄創建標記文件,命名規則:thumb_{md5(目標文件路徑)}。<?php
define('WWW_PATH', dirname(dirname(__FILE__)));
require(WWW_PATH.'/PicThumb.class.php');
require(WWW_PATH.'/ThumbConfig.php');
$logfile = WWW_PATH.'/createthumb.log';
$source_path = WWW_PATH.'/upload/';
$dest_path = WWW_PATH.'/supload/';
$path = isset($_GET['path']) ? $_GET['path'] : '';
if (!$path) exit();
// 解析請求路徑,獲取類型與配置
$relative_url = str_replace($dest_path, '', WWW_PATH.$path);
$type = substr($relative_url, 0, strpos($relative_url, '/'));
$config = isset($thumb_config[$type]) ? $thumb_config[$type] : '';
if (!$config || !isset($config['fromdir'])) exit();
$source = str_replace('/'.$type.'/', '/'.$config['fromdir'].'/', $source_path.$relative_url);
$dest = $dest_path.$relative_url;
if (!file_exists($source)) exit(); // 原圖不存在
// --- 高併發鎖機制 ---
$processing_flag = '/tmp/thumb_'.md5($dest);
$is_wait = 0;
$wait_timeout = 5;
$starttime = time();
if (!file_exists($processing_flag)) {
file_put_contents($processing_flag, 1, LOCK_EX); // 原子寫入
} else {
$is_wait = 1;
}
if ($is_wait) {
while (file_exists($processing_flag)) {
if (time() - $starttime > $wait_timeout) exit(); // 超時退出
usleep(300000); // 休眠 300ms
}
// 生成完成,直接輸出
if (file_exists($dest)) {
ob_clean();
header('Content-Type: '.mime_content_type($dest));
exit(file_get_contents($dest));
} else {
exit(); // 生成失敗
}
}
// --- 執行生成 ---
$obj = new PicThumb($logfile);
$obj->set_config($config);
$create_flag = $obj->create_thumb($source, $dest);
unlink($processing_flag); // 釋放鎖
if ($create_flag) {
ob_clean();
header('Content-Type: '.mime_content_type($dest));
exit(file_get_contents($dest));
}
?>
file_put_contents(..., LOCK_EX) 確保標記創建的原子性。通過這種「首次請求生成,並發請求等待」的機制,有效解決了縮略圖按需生成時的高併發衝突,兼顧了存儲效率與系統穩定性。