upload project
This commit is contained in:
commit
06961cae04
422 changed files with 110626 additions and 0 deletions
296
utils/image-cache-manager.js
Normal file
296
utils/image-cache-manager.js
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
/**
|
||||
* 图片缓存管理器
|
||||
* 实现头像和聊天图片的缓存功能
|
||||
*/
|
||||
|
||||
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;
|
||||
Loading…
Add table
Add a link
Reference in a new issue