283 lines
6.7 KiB
JavaScript
283 lines
6.7 KiB
JavaScript
|
|
/**
|
|||
|
|
* 图片缓存管理器
|
|||
|
|
* 实现头像和聊天图片的缓存功能
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
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;
|