/** * 图片缓存管理器 * 实现头像和聊天图片的缓存功能 */ 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 || []); } } 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) { 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); }); 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:')) { return url; // 不缓存base64等 } // 对于本地路径前缀,直接返回 if (url.startsWith('/')) { return url; } // 确保URL是完整的,添加https前缀如果缺少 let fullUrl = url; if (!fullUrl.startsWith('http://') && !fullUrl.startsWith('https://')) { fullUrl = 'https://' + url; } const key = this.generateCacheKey(fullUrl, type); // 检查是否已缓存 if (this.isCached(fullUrl, type)) { return this.getCachedPath(fullUrl, type); } try { // 下载图片 const downloadResult = await new Promise((resolve, reject) => { wx.downloadFile({ url: fullUrl, timeout: 15000, // 增加超时时间 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(); return localPath; } else { console.warn('⚠️ 图片下载失败,状态码:', downloadResult.statusCode, 'URL:', fullUrl); // 如果下载失败,返回完整的URL return fullUrl; } } catch (error) { console.error('❌ 缓存图片失败:', fullUrl, error); // 在缓存失败时,返回完整的URL return fullUrl; } } /** * 缓存头像 */ 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); } 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(); } /** * 清理指定类型的缓存 */ 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); }); this.saveCache(); } } // 创建单例实例 const imageCacheManager = new ImageCacheManager(); module.exports = imageCacheManager;