Download src/utils/imageCache.js from ahutchen/music: direct link, hf CLI and curl.
- Browser
- Download file 7.33 kB
-
https://huggingface.co/spaces/ahutchen/music/resolve/main/src/utils/imageCache.js
- Command line
-
hf download hf://spaces/ahutchen/music/src/utils/imageCache.js
-
curl -L -o imageCache.js https://huggingface.co/spaces/ahutchen/music/resolve/main/src/utils/imageCache.js
7.33 kB
| // 图像缓存管理器 - 解决跨域问题并实现本地缓存 | |
| class ImageCacheManager { | |
| constructor() { | |
| this.cache = new Map() | |
| this.cacheName = 'music-images-v1' | |
| this.maxCacheSize = 100 // 最多缓存100张图片 | |
| this.cacheExpiry = 7 * 24 * 60 * 60 * 1000 // 7天过期 | |
| this.init() | |
| } | |
| async init() { | |
| // 清理过期的缓存 | |
| await this.cleanupExpiredCache() | |
| } | |
| // 获取图片的缓存键 | |
| getCacheKey(url) { | |
| return `img_${this.hashUrl(url)}` | |
| } | |
| // 简单的URL哈希函数 | |
| hashUrl(url) { | |
| let hash = 0 | |
| for (let i = 0; i < url.length; i++) { | |
| const char = url.charCodeAt(i) | |
| hash = ((hash << 5) - hash) + char | |
| hash = hash & hash // 转换为32位整数 | |
| } | |
| return Math.abs(hash).toString(36) | |
| } | |
| // 将图片转换为base64并缓存 - 改进跨域处理 | |
| async cacheImage(url, metadata = {}) { | |
| try { | |
| // 检查URL是否为空或无效 | |
| if (!url || typeof url !== 'string') { | |
| return this.getDefaultImage() | |
| } | |
| const cacheKey = this.getCacheKey(url) | |
| // 检查内存缓存 | |
| if (this.cache.has(cacheKey)) { | |
| return this.cache.get(cacheKey).dataUrl | |
| } | |
| // 检查localStorage缓存 | |
| const cached = localStorage.getItem(cacheKey) | |
| if (cached) { | |
| try { | |
| const cachedData = JSON.parse(cached) | |
| if (Date.now() - cachedData.timestamp < this.cacheExpiry) { | |
| this.cache.set(cacheKey, cachedData) | |
| return cachedData.dataUrl | |
| } | |
| } catch (e) { | |
| // 缓存数据损坏,删除 | |
| localStorage.removeItem(cacheKey) | |
| } | |
| } | |
| // 直接使用原始URL,不使用代理 | |
| let finalUrl = url | |
| // 使用Canvas方式转换图片 | |
| const dataUrl = await this.convertToDataUrl(finalUrl) | |
| // 保存到缓存 | |
| const cacheData = { | |
| dataUrl, | |
| timestamp: Date.now(), | |
| originalUrl: url, | |
| metadata | |
| } | |
| this.cache.set(cacheKey, cacheData) | |
| // 保存到localStorage | |
| try { | |
| if (this.cache.size <= this.maxCacheSize) { | |
| localStorage.setItem(cacheKey, JSON.stringify(cacheData)) | |
| } | |
| } catch (e) { | |
| // localStorage空间不足,清理一些旧缓存 | |
| await this.cleanupOldCache() | |
| try { | |
| localStorage.setItem(cacheKey, JSON.stringify(cacheData)) | |
| } catch (e) { | |
| console.warn('无法保存图片到localStorage:', e) | |
| } | |
| } | |
| return dataUrl | |
| } catch (error) { | |
| console.error('缓存图片失败:', error) | |
| return this.getDefaultImage() | |
| } | |
| } | |
| // 检查是否为跨域URL | |
| isCrossOrigin(url) { | |
| try { | |
| const urlObj = new URL(url, window.location.href) | |
| return urlObj.origin !== window.location.origin | |
| } catch (e) { | |
| // 如果URL解析失败,假设为跨域 | |
| return true | |
| } | |
| } | |
| // 将图片转换为DataUrl | |
| async convertToDataUrl(url) { | |
| return new Promise((resolve, reject) => { | |
| const img = new Image() | |
| img.crossOrigin = 'anonymous' | |
| img.onload = () => { | |
| try { | |
| const canvas = document.createElement('canvas') | |
| const ctx = canvas.getContext('2d') | |
| canvas.width = img.naturalWidth | |
| canvas.height = img.naturalHeight | |
| ctx.drawImage(img, 0, 0) | |
| const result = canvas.toDataURL('image/jpeg', 0.8) | |
| resolve(result) | |
| } catch (error) { | |
| console.warn('Canvas转换失败,使用原始URL:', error) | |
| resolve(url) | |
| } | |
| } | |
| img.onerror = () => { | |
| console.warn('图片加载失败,使用默认图片') | |
| resolve(this.getDefaultImage()) | |
| } | |
| // 设置超时 | |
| setTimeout(() => { | |
| console.warn('图片加载超时,使用默认图片') | |
| resolve(this.getDefaultImage()) | |
| }, 10000) | |
| img.src = url | |
| }) | |
| } | |
| // 获取默认图片 - 返回 null,让组件使用 fas fa-music 图标 | |
| getDefaultImage() { | |
| return null | |
| } | |
| // 获取缓存的图片 | |
| async getCachedImage(url) { | |
| const cacheKey = this.getCacheKey(url) | |
| // 检查内存缓存 | |
| if (this.cache.has(cacheKey)) { | |
| return this.cache.get(cacheKey).dataUrl | |
| } | |
| // 检查localStorage缓存 | |
| const cached = localStorage.getItem(cacheKey) | |
| if (cached) { | |
| try { | |
| const cachedData = JSON.parse(cached) | |
| if (Date.now() - cachedData.timestamp < this.cacheExpiry) { | |
| this.cache.set(cacheKey, cachedData) | |
| return cachedData.dataUrl | |
| } else { | |
| // 缓存过期,删除 | |
| localStorage.removeItem(cacheKey) | |
| } | |
| } catch (e) { | |
| localStorage.removeItem(cacheKey) | |
| } | |
| } | |
| return null | |
| } | |
| // 预加载图片 | |
| async preloadImage(url, metadata = {}) { | |
| return this.cacheImage(url, metadata) | |
| } | |
| // 清理过期缓存 | |
| async cleanupExpiredCache() { | |
| const keys = Object.keys(localStorage) | |
| const now = Date.now() | |
| keys.forEach(key => { | |
| if (key.startsWith('img_')) { | |
| try { | |
| const cached = JSON.parse(localStorage.getItem(key)) | |
| if (now - cached.timestamp > this.cacheExpiry) { | |
| localStorage.removeItem(key) | |
| } | |
| } catch (e) { | |
| localStorage.removeItem(key) | |
| } | |
| } | |
| }) | |
| } | |
| // 清理旧缓存(释放空间) | |
| async cleanupOldCache() { | |
| const keys = Object.keys(localStorage) | |
| const imageKeys = keys.filter(key => key.startsWith('img_')) | |
| // 按时间戳排序,删除最旧的一半 | |
| const cached = imageKeys.map(key => { | |
| try { | |
| const data = JSON.parse(localStorage.getItem(key)) | |
| return { key, timestamp: data.timestamp } | |
| } catch (e) { | |
| return { key, timestamp: 0 } | |
| } | |
| }).sort((a, b) => a.timestamp - b.timestamp) | |
| const toDelete = cached.slice(0, Math.floor(cached.length / 2)) | |
| toDelete.forEach(item => { | |
| localStorage.removeItem(item.key) | |
| this.cache.delete(item.key) | |
| }) | |
| } | |
| // 清空所有缓存 | |
| async clearCache() { | |
| const keys = Object.keys(localStorage) | |
| keys.forEach(key => { | |
| if (key.startsWith('img_')) { | |
| localStorage.removeItem(key) | |
| } | |
| }) | |
| this.cache.clear() | |
| } | |
| // 获取缓存统计信息 | |
| getCacheStats() { | |
| const keys = Object.keys(localStorage) | |
| const imageKeys = keys.filter(key => key.startsWith('img_')) | |
| let totalSize = 0 | |
| imageKeys.forEach(key => { | |
| totalSize += localStorage.getItem(key).length | |
| }) | |
| return { | |
| totalImages: imageKeys.length, | |
| memoryCache: this.cache.size, | |
| totalSize: (totalSize / 1024 / 1024).toFixed(2) + 'MB' | |
| } | |
| } | |
| } | |
| // 创建全局实例 | |
| export const imageCacheManager = new ImageCacheManager() | |
| // 导出便捷方法 | |
| export const cacheImage = (url, metadata) => imageCacheManager.cacheImage(url, metadata) | |
| export const getCachedImage = (url) => imageCacheManager.getCachedImage(url) | |
| export const preloadImage = (url, metadata) => imageCacheManager.preloadImage(url, metadata) | |
| export const clearImageCache = () => imageCacheManager.clearCache() | |
| export const getImageCacheStats = () => imageCacheManager.getCacheStats() | |
| export default imageCacheManager |