PHP实现数据库Blob存储头像的缓存与显示方案问询
嘿,我完全懂你现在的困扰——包含头像的Div频繁更新,每次都要去数据库查Blob,这冗余的操作确实挺耗资源的。下面给你几个落地性很强的缓存方案,直接就能上手实现:
方案1:PHP独立头像接口 + 浏览器缓存(最推荐)
这是最常用的方案,把头像读取逻辑单独做成一个PHP接口,利用浏览器自带的缓存机制,从根源上避免重复请求和数据库查询。
步骤1:编写get_avatar.php接口脚本
这个脚本负责处理头像的读取、缓存校验和返回:
<?php // 获取用户ID,做安全校验 $user_id = isset($_GET['id']) ? (int)$_GET['id'] : 0; if (!$user_id) exit; // 连接数据库(这里用PDO示例,你可以换成自己的连接方式) $pdo = new PDO('mysql:host=localhost;dbname=your_database', 'your_username', 'your_password'); // 从数据库获取头像Blob和最后更新时间(建议给用户表加个`avatar_updated_at`字段) $stmt = $pdo->prepare("SELECT avatar_blob, avatar_updated_at FROM users WHERE id = ?"); $stmt->execute([$user_id]); $user_data = $stmt->fetch(PDO::FETCH_ASSOC); if (!$user_data) exit; // 处理浏览器缓存校验 $last_modified_time = strtotime($user_data['avatar_updated_at']); $etag = md5($user_data['avatar_blob']); // 用内容哈希做ETag,确保内容变化时缓存失效 // 设置缓存响应头 header("Last-Modified: " . gmdate("D, d M Y H:i:s", $last_modified_time) . " GMT"); header("ETag: \"$etag\""); // 检查请求头,如果缓存有效,直接返回304 Not Modified if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $last_modified_time || isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) == "\"$etag\"") { header("HTTP/1.1 304 Not Modified"); exit; } // 缓存无效时,返回头像数据 header("Content-Type: image/jpeg"); // 根据你的头像实际格式调整,比如image/png header("Cache-Control: public, max-age=86400"); // 设置缓存1天,可按需调整 echo $user_data['avatar_blob']; ?>
步骤2:在页面中引用该接口
把原来直接输出Blob的Div改成用<img>标签引用这个接口:
<div class="avatar-container"> <img src="get_avatar.php?id=<?php echo $current_user_id; ?>" alt="User Avatar"> </div>
这样浏览器第一次加载后会把头像缓存起来,后续Div更新时,只要用户ID不变且头像没更新,浏览器会直接用本地缓存,不会再请求服务器,自然也就不会重复查数据库了。
方案2:服务器端文件缓存(减少数据库依赖)
如果担心数据库查询量还是太大,可以把Blob转换成本地文件缓存,下次直接读取文件,跳过数据库查询。
实现代码示例
<?php function serve_avatar($user_id) { $cache_dir = './avatar_cache/'; // 创建缓存目录(如果不存在) if (!is_dir($cache_dir)) mkdir($cache_dir, 0755); // 缓存文件名,用用户ID命名 $cache_file = $cache_dir . $user_id . '.jpg'; // 后缀根据实际头像格式调整 $cache_expire = 86400; // 缓存有效期1天 // 检查缓存文件是否存在且未过期 if (file_exists($cache_file) && (time() - filemtime($cache_file)) < $cache_expire) { header("Content-Type: image/jpeg"); readfile($cache_file); exit; } // 缓存失效或不存在,从数据库读取并生成缓存 $pdo = new PDO('mysql:host=localhost;dbname=your_database', 'your_username', 'your_password'); $stmt = $pdo->prepare("SELECT avatar_blob FROM users WHERE id = ?"); $stmt->execute([$user_id]); $avatar_blob = $stmt->fetchColumn(); if ($avatar_blob) { // 写入缓存文件 file_put_contents($cache_file, $avatar_blob); header("Content-Type: image/jpeg"); echo $avatar_blob; } } // 处理请求 $user_id = isset($_GET['id']) ? (int)$_GET['id'] : 0; if ($user_id) serve_avatar($user_id); ?>
注意:当用户更新头像时,一定要删除对应的缓存文件,这样下次会重新从数据库读取并生成新缓存。
方案3:前端内存缓存(适合高频动态更新场景)
如果你的Div是用JavaScript动态更新的(比如聊天窗口切换联系人),可以在前端用内存缓存已加载的头像,避免重复请求。
JS示例代码
// 全局对象存储已加载的头像URL const avatarCache = {}; function renderAvatar(userId, containerElement) { // 先检查缓存 if (avatarCache[userId]) { containerElement.innerHTML = `<img src="${avatarCache[userId]}" alt="User Avatar">`; return; } // 缓存不存在,请求接口并存储 fetch(`get_avatar.php?id=${userId}`) .then(response => response.blob()) .then(blob => { const avatarUrl = URL.createObjectURL(blob); avatarCache[userId] = avatarUrl; containerElement.innerHTML = `<img src="${avatarUrl}" alt="User Avatar">`; }) .catch(err => console.error('加载头像失败:', err)); } // 调用示例:更新某个Div的头像 renderAvatar(123, document.querySelector('.chat-avatar'));
关键注意事项
- 当用户更新头像时,必须让缓存失效:
- 方案1:更新用户表的
avatar_updated_at字段 - 方案2:删除对应用户ID的缓存文件
- 方案3:从
avatarCache对象中删除该用户ID的缓存项
- 方案1:更新用户表的
- 确保设置正确的
Content-Type,根据你的头像实际格式(JPG/PNG/WebP等)调整
内容的提问来源于stack exchange,提问作者Albin wärme
相关产品推荐
相关产品推荐

