miniprogramme/utils/image-cache-manager.js
2025-09-12 16:08:17 +08:00

283 lines
No EOL
6.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 图片缓存管理器
* 实现头像和聊天图片的缓存功能
*/
class ImageCacheManager {
constructor() {
this.cache = new Map();
this.cacheExpiry = new Map();
this.maxCacheSize = 100; // 最大缓存数量
this.cacheExpiryTime = 7 * 24 * 60 * 60 * 1000; // 7天缓存过期时间
this.avatarCacheExpiryTime = 30 * 24 * 60 * 60 * 1000; // 头像30天缓存过期时间
// 初始化缓存
this.initCache();
}
/**
* 初始化缓存,从本地存储加载
*/
initCache() {
try {
const cachedData = wx.getStorageSync('image_cache');
if (cachedData) {
const data = JSON.parse(cachedData);
this.cache = new Map(data.cache || []);
this.cacheExpiry = new Map(data.expiry || []);
console.log('📦 图片缓存初始化完成,缓存数量:', this.cache.size);
}
} catch (error) {
console.error('❌ 初始化图片缓存失败:', error);
}
}
/**
* 保存缓存到本地存储
*/
saveCache() {
try {
const data = {
cache: Array.from(this.cache.entries()),
expiry: Array.from(this.cacheExpiry.entries())
};
wx.setStorageSync('image_cache', JSON.stringify(data));
} catch (error) {
console.error('❌ 保存图片缓存失败:', error);
}
}
/**
* 清理过期缓存
*/
cleanExpiredCache() {
const now = Date.now();
const expiredKeys = [];
for (const [key, expiryTime] of this.cacheExpiry.entries()) {
if (now > expiryTime) {
expiredKeys.push(key);
}
}
expiredKeys.forEach(key => {
this.cache.delete(key);
this.cacheExpiry.delete(key);
});
if (expiredKeys.length > 0) {
console.log('🧹 清理过期缓存:', expiredKeys.length, '个');
this.saveCache();
}
}
/**
* 清理超出最大数量的缓存
*/
cleanExcessCache() {
if (this.cache.size <= this.maxCacheSize) return;
// 按过期时间排序,删除最旧的
const sortedEntries = Array.from(this.cacheExpiry.entries())
.sort((a, b) => a[1] - b[1]);
const excessCount = this.cache.size - this.maxCacheSize;
const keysToDelete = sortedEntries.slice(0, excessCount).map(([key]) => key);
keysToDelete.forEach(key => {
this.cache.delete(key);
this.cacheExpiry.delete(key);
});
console.log('🧹 清理超出限制的缓存:', keysToDelete.length, '个');
this.saveCache();
}
/**
* 生成缓存键
*/
generateCacheKey(url, type = 'image') {
return `${type}_${url}`;
}
/**
* 检查缓存是否存在且有效
*/
isCached(url, type = 'image') {
const key = this.generateCacheKey(url, type);
const expiryTime = this.cacheExpiry.get(key);
if (!expiryTime) return false;
if (Date.now() > expiryTime) {
// 缓存已过期,删除
this.cache.delete(key);
this.cacheExpiry.delete(key);
return false;
}
return this.cache.has(key);
}
/**
* 获取缓存的图片路径
*/
getCachedPath(url, type = 'image') {
const key = this.generateCacheKey(url, type);
return this.cache.get(key);
}
/**
* 缓存图片
*/
async cacheImage(url, type = 'image') {
if (!url || url.startsWith('data:') || url.startsWith('/')) {
return url; // 不缓存base64、本地路径等
}
const key = this.generateCacheKey(url, type);
// 检查是否已缓存
if (this.isCached(url, type)) {
console.log('📦 使用缓存图片:', url);
return this.getCachedPath(url, type);
}
try {
console.log('📥 开始缓存图片:', url);
// 下载图片
const downloadResult = await new Promise((resolve, reject) => {
wx.downloadFile({
url: url,
success: resolve,
fail: reject
});
});
if (downloadResult.statusCode === 200) {
const localPath = downloadResult.tempFilePath;
// 设置缓存过期时间
const expiryTime = type === 'avatar'
? Date.now() + this.avatarCacheExpiryTime
: Date.now() + this.cacheExpiryTime;
// 保存到缓存
this.cache.set(key, localPath);
this.cacheExpiry.set(key, expiryTime);
// 清理过期和超量缓存
this.cleanExpiredCache();
this.cleanExcessCache();
console.log('✅ 图片缓存成功:', url, '->', localPath);
return localPath;
} else {
console.warn('⚠️ 图片下载失败,状态码:', downloadResult.statusCode);
return url;
}
} catch (error) {
console.error('❌ 缓存图片失败:', url, error);
return url;
}
}
/**
* 缓存头像
*/
async cacheAvatar(url) {
return this.cacheImage(url, 'avatar');
}
/**
* 更新头像缓存当头像URL变化时
*/
async updateAvatarCache(oldUrl, newUrl) {
if (oldUrl && oldUrl !== newUrl) {
const oldKey = this.generateCacheKey(oldUrl, 'avatar');
this.cache.delete(oldKey);
this.cacheExpiry.delete(oldKey);
console.log('🔄 更新头像缓存:', oldUrl, '->', newUrl);
}
if (newUrl) {
return await this.cacheAvatar(newUrl);
}
return newUrl;
}
/**
* 预加载图片(用于聊天中的图片)
*/
async preloadImage(url) {
return this.cacheImage(url, 'chat_image');
}
/**
* 获取缓存统计信息
*/
getCacheStats() {
const now = Date.now();
let avatarCount = 0;
let imageCount = 0;
let expiredCount = 0;
for (const [key, expiryTime] of this.cacheExpiry.entries()) {
if (key.startsWith('avatar_')) {
avatarCount++;
} else if (key.startsWith('image_') || key.startsWith('chat_image_')) {
imageCount++;
}
if (now > expiryTime) {
expiredCount++;
}
}
return {
total: this.cache.size,
avatar: avatarCount,
image: imageCount,
expired: expiredCount,
maxSize: this.maxCacheSize
};
}
/**
* 清空所有缓存
*/
clearAllCache() {
this.cache.clear();
this.cacheExpiry.clear();
this.saveCache();
console.log('🗑️ 清空所有图片缓存');
}
/**
* 清理指定类型的缓存
*/
clearCacheByType(type) {
const keysToDelete = [];
for (const key of this.cache.keys()) {
if (key.startsWith(`${type}_`)) {
keysToDelete.push(key);
}
}
keysToDelete.forEach(key => {
this.cache.delete(key);
this.cacheExpiry.delete(key);
});
console.log(`🗑️ 清理${type}类型缓存:`, keysToDelete.length, '个');
this.saveCache();
}
}
// 创建单例实例
const imageCacheManager = new ImageCacheManager();
module.exports = imageCacheManager;