diff --git a/src/utils/imageCacheManager.js b/src/utils/imageCacheManager.js index 089ba85..2f7cfaa 100644 --- a/src/utils/imageCacheManager.js +++ b/src/utils/imageCacheManager.js @@ -4,8 +4,8 @@ class ImageCacheManager { this.cacheVersionKey = 'likei-image-cache-version' this.currentAppVersion = import.meta.env.VITE_APP_VERSION || '1.0.0' this.cacheName = `likei-images-v${this.currentAppVersion}` - this.maxCacheItems = 100 // 限制最多缓存50张图片(可根据需要调整) - this.maxCacheSize = 100 * 1024 * 1024 // 限制总大小50MB(可根据需要调整) + this.maxCacheItems = 100 // 最大缓存数量(100张) + this.maxCacheSize = 100 * 1024 * 1024 // 最大缓存大小(100MB) this.init() } @@ -35,23 +35,91 @@ class ImageCacheManager { try { const keys = await cache.keys() - // 检查数量限制 - if (keys.length > this.maxCacheItems) { - // 删除最旧的缓存项 - const itemsToRemove = keys.length - this.maxCacheItems - for (let i = 0; i < itemsToRemove; i++) { - await cache.delete(keys[i]) - console.log(`已删除超出限制的缓存项: ${keys[i].url}`) + // 如果同时满足数量和大小限制,则无需处理 + if (keys.length <= this.maxCacheItems) { + // 快速估算大小检查(基于headers) + let estimatedTotalSize = 0 + let needDetailedCheck = false + + for (const key of keys) { + const response = await cache.match(key) + if (response) { + const contentLength = response.headers.get('content-length') + if (contentLength) { + estimatedTotalSize += parseInt(contentLength, 10) + } else { + // 如果有任何一项没有content-length头,需要详细检查 + needDetailedCheck = true + break + } + } + } + + // 如果估计大小在限制内且不需要详细检查,则直接返回 + if (!needDetailedCheck && estimatedTotalSize <= this.maxCacheSize) { + return } } - // 可选:检查大小限制(需要估算) - // 这里简化处理,实际项目中可以更精确计算 + // 获取所有缓存项及其大小信息 + const cacheEntries = [] + for (const key of keys) { + try { + const response = await cache.match(key) + if (response) { + const blob = await response.blob() + cacheEntries.push({ + key, + size: blob.size, + response, + }) + } + } catch (e) { + console.warn('无法获取缓存项大小:', key.url) + } + } + + // 计算当前总数和总大小 + let totalItems = cacheEntries.length + let totalSize = cacheEntries.reduce((sum, entry) => sum + entry.size, 0) + + // 确定需要删除的项数(基于数量限制) + let itemsToRemoveCount = Math.max(0, totalItems - this.maxCacheItems) + + // 确定需要删除的额外项数(基于大小限制) + let sizeToRemove = Math.max(0, totalSize - this.maxCacheSize) + + // 综合两个限制条件,确定实际需要删除的项 + let itemsToDelete = [] + if (itemsToRemoveCount > 0 || sizeToRemove > 0) { + // 按照最旧的顺序排序(keys已经是按顺序的) + // 逐个删除直到满足两个条件 + let removedSize = 0 + let removedCount = 0 + + for (const entry of cacheEntries) { + // 如果已经满足两个条件,则停止删除 + if (removedCount >= itemsToRemoveCount && totalSize - removedSize <= this.maxCacheSize) { + break + } + + itemsToDelete.push(entry.key) + removedSize += entry.size + removedCount++ + } + } + + // 执行删除操作 + for (const key of itemsToDelete) { + await cache.delete(key) + console.log(`已删除超出限制的缓存项: ${key.url}`) + } } catch (error) { console.warn('检查缓存限制时出错:', error) } } + // 清除全部旧图片缓存 async clearOldCaches() { if (!('caches' in window)) return @@ -68,7 +136,7 @@ class ImageCacheManager { } } - // 更严格的判断是否为本地构建资源 + //判断是否为适合缓存资源 isBuildAsset(url) { try { // 如果是相对路径,肯定是本地资源 @@ -116,9 +184,6 @@ class ImageCacheManager { const cachedResponse = await cache.match(url) if (!cachedResponse) { - // 先检查并清理超出限制的缓存 - // await this.enforceCacheLimits(cache) - const response = await fetch(url, { mode: 'cors', credentials: 'omit', @@ -126,6 +191,9 @@ class ImageCacheManager { if (response.ok) { await cache.put(url, response.clone()) console.log(`图片已缓存: ${url}`) + + // 添加新图片后立即检查并强制执行限制 + await this.enforceCacheLimits(cache) } } } @@ -181,6 +249,7 @@ class ImageCacheManager { }) } + // 从网络加载图片 async loadImageFromNetwork(src, resolve, reject) { try { // 对于OSS资源,先尝试直接创建Image对象(绕过fetch的CORS问题) @@ -235,26 +304,31 @@ class ImageCacheManager { // 为OSS图片创建缓存的特殊处理 async cacheImageFromImage(src, img) { try { + // 检查浏览器是否支持缓存API以及资源是否适合缓存 if (!('caches' in window) || !this.isBuildAsset(src)) return const cache = await caches.open(this.cacheName) const cachedResponse = await cache.match(src) + // 只有当图片尚未缓存时才进行缓存操作 if (!cachedResponse) { - // 创建一个模拟的响应来缓存 + // 创建canvas元素来处理图片 const canvas = document.createElement('canvas') canvas.width = img.width canvas.height = img.height const ctx = canvas.getContext('2d') - ctx.drawImage(img, 0, 0) + ctx.drawImage(img, 0, 0) // 将图片绘制到canvas上 + // 将canvas转换为blob对象 canvas.toBlob(async (blob) => { if (blob) { + // 创建一个新的Response对象来模拟网络响应 const response = new Response(blob, { headers: { 'Content-Type': 'image/png', }, }) + // 将图片存入缓存 await cache.put(src, response.clone()) console.log(`OSS图片已缓存: ${src}`) } diff --git a/src/utils/imagePreloader.js b/src/utils/imagePreloader.js index f20dead..e08e0d5 100644 --- a/src/utils/imagePreloader.js +++ b/src/utils/imagePreloader.js @@ -5,6 +5,7 @@ export const preloadImages = (sources, options = {}) => { const { useSmartCache = false } = options let urls = [] + // 支持数组和对象形式的图片源 if (Array.isArray(sources)) { urls = sources } else if (typeof sources === 'object') {