296 lines
6.5 KiB
JavaScript
296 lines
6.5 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 || []);
|
||
|
||
}
|
||
} 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;
|