Files
TrueGrowth/docs/UNIFIED_CACHE_IMPLEMENTATION_SUMMARY.md

24 KiB
Raw Permalink Blame History

统一图片缓存系统实施总结

概述

本次实施完成了一个全新的统一图片缓存系统,整合了 Service Worker 和应用层的缓存管理,实现了智能图片传递、元数据记录、缓存配额监控等功能。


系统架构

整体架构图

┌─────────────────────────────────────────────────────────────────┐
│                         用户界面层                                │
├─────────────────────────────────────────────────────────────────┤
│  CacheQuotaProvider  │  MediaLibrary  │  AIInputBar  │  Canvas  │
└──────────┬──────────────────┬─────────────────┬────────────────┘
           │                  │                 │
           ▼                  ▼                 ▼
┌─────────────────────────────────────────────────────────────────┐
│                      React Hooks 层                               │
├─────────────────────────────────────────────────────────────────┤
│  useUnifiedCache  │  useCacheStats  │  useCacheQuotaMonitor     │
└──────────┬──────────────────────────────────────────────────────┘
           │
           ▼
┌─────────────────────────────────────────────────────────────────┐
│                   unifiedCacheService (核心)                      │
│  - IndexedDB 元数据管理                                           │
│  - SW 通信协调                                                    │
│  - 智能图片传递                                                   │
│  - 数据迁移                                                       │
└──────────┬───────────────────────────┬──────────────────────────┘
           │                           │
           ▼                           ▼
┌──────────────────────┐    ┌──────────────────────────────────┐
│   IndexedDB          │    │    Service Worker                 │
│   drawnix-unified-   │    │  - Cache API (快速响应)           │
│   cache              │    │  - 图片拦截和缓存                  │
│  - images (元数据)    │    │  - 配额监控                       │
│  - migrations (标记) │    │  - PostMessage 通信               │
└──────────────────────┘    └──────────────────────────────────┘

双层存储策略

  1. Service Worker Cache API

    • 用途: 快速图片响应
    • 内容: 只存储图片 Blob 数据
    • 优势: 拦截网络请求,无需应用层介入
  2. IndexedDB (drawnix-unified-cache)

    • 用途: 元数据管理
    • 内容: URL、缓存时间、模型、提示词、参数等
    • 优势: 查询灵活,支持复杂查询

核心功能实现

1. Service Worker 图片缓存拦截

文件: apps/web/src/sw/index.ts

实现要点:

  • 拦截所有图片请求JPEG, PNG, WebP, GIF
  • 优先返回缓存,缓存未命中时从网络获取
  • 缓存成功后通知主线程注册元数据
  • 监控存储配额,接近上限时发出警告

关键代码:

// 拦截图片请求
self.addEventListener('fetch', (event) => {
  const request = event.request;
  if (request.method === 'GET' && isImageRequest(request)) {
    event.respondWith(handleImageRequest(request));
  }
});

// 缓存后通知主线程
async function notifyImageCached(url, size, mimeType) {
  const clients = await self.clients.matchAll();
  clients.forEach(client => {
    client.postMessage({
      type: 'IMAGE_CACHED',
      url, size, mimeType,
      timestamp: Date.now(),
    });
  });
}

消息协议:

  • IMAGE_CACHED: SW → Main (图片已缓存)
  • QUOTA_WARNING: SW → Main (配额警告)
  • DELETE_CACHE: Main → SW (删除缓存)
  • DELETE_CACHE_BATCH: Main → SW (批量删除)
  • CLEAR_ALL_CACHE: Main → SW (清空所有缓存)

2. 统一缓存管理服务

文件: packages/drawnix/src/services/unified-cache-service.ts

核心功能:

2.1 元数据注册

async registerImageMetadata(
  url: string,
  metadata: {
    taskId: string;
    model?: string;
    prompt?: string;
    params?: any;
  }
): Promise<void>
  • 在 IndexedDB 中记录图片元数据
  • 支持任务 ID、模型、提示词、参数等信息
  • 自动记录缓存时间和最后访问时间

2.2 智能图片传递

async getImageForAI(
  url: string,
  options?: {
    maxAge?: number;      // 默认 24 小时
    maxSize?: number;     // 默认 3MB
    quality?: number;     // 默认 0.8
  }
): Promise<ImageData>

决策逻辑:

缓存时间 < maxAge (默认 24h)
  ↓
返回 { type: 'url', value: url }

缓存时间 ≥ maxAge
  ↓
从缓存获取图片 Blob
  ↓
图片大小 > maxSize (默认 3MB)?
  ↓ 是
压缩图片 (quality=0.8)
  ↓
转换为 base64
  ↓
返回 { type: 'base64', value: base64String }

使用示例:

// 在 canvas-insertion.ts 中
const imageData = await unifiedCacheService.getImageForAI(imageUrl);
// imageData 可能是 { type: 'url', value: 'https://...' }
// 或 { type: 'base64', value: 'data:image/png;base64,...' }
const image = await loadHTMLImageElement(imageData.value, false);

2.3 数据迁移

private async migrateFromLegacyDatabases(): Promise<void>
  • 自动检测旧数据库 (aitu-media-cache, aitu-url-cache)
  • 将数据转换为新格式并导入
  • 迁移完成后删除旧数据库
  • 使用 localStorage 标记避免重复迁移

迁移流程:

启动时检查 localStorage['CACHE_MIGRATION_DONE']
  ↓ 未标记
检测旧数据库
  ↓ 存在
读取旧数据 → 转换格式 → 写入新数据库
  ↓
删除旧数据库
  ↓
设置迁移完成标记

3. React Hooks 封装

文件: packages/drawnix/src/hooks/useUnifiedCache.ts

3.1 useUnifiedCache(url)

单个图片的缓存管理

const {
  cacheInfo,        // { isCached, cachedAt, size, ... }
  isLoading,        // 加载状态
  isCached,         // 是否已缓存
  cacheAge,         // 缓存时长(毫秒)
  cacheImage,       // 手动缓存函数
  deleteCache,      // 删除缓存函数
  reload,           // 重新加载缓存信息
} = useUnifiedCache(imageUrl);

特点:

  • 自动订阅缓存变化
  • 响应式更新 UI
  • 错误处理和加载状态

3.2 useCacheStats()

整体缓存统计

const {
  totalCount,       // 总缓存数量
  storageUsage,     // { usage, quota, percentage }
  isLoading,        // 加载状态
  reload,           // 刷新统计
  formatSize,       // 格式化大小函数
} = useCacheStats();

3.3 useCacheQuotaMonitor(callback)

配额监控

const {
  isQuotaExceeded,  // 是否超过配额
  resetQuotaFlag,   // 重置标记
} = useCacheQuotaMonitor(() => {
  // 配额超限回调
  showDialog();
});

4. 缓存配额提供者

文件: packages/drawnix/src/components/cache-quota-provider/CacheQuotaProvider.tsx

功能:

  • 全局监听缓存配额事件
  • 配额超限时显示 TDesign 确认对话框
  • 提供 "打开素材库" 和 "稍后处理" 选项
  • 防止重复弹窗

集成:

// 在 drawnix.tsx 中
<CacheQuotaProvider onOpenMediaLibrary={handleOpenMediaLibrary}>
  <App />
</CacheQuotaProvider>

对话框内容:

标题: 缓存空间已满
内容: 图片缓存空间已满,无法继续缓存新图片。是否打开素材库清理缓存?
按钮: [打开素材库] [稍后处理]
主题: warning

5. 任务完成时自动注册元数据

文件: packages/drawnix/src/hooks/useTaskExecutor.ts

实现位置:

  1. 正常任务完成后 (line 419-432)
  2. 恢复视频任务完成后 (line 173-186)

注册逻辑:

// 任务完成后
if (result.url) {
  try {
    await unifiedCacheService.registerImageMetadata(result.url, {
      taskId: task.id,
      model: task.params.model,
      prompt: task.params.prompt,
      params: task.params,
    });
    console.log(`[TaskExecutor] Registered metadata for task ${taskId}`);
  } catch (error) {
    console.error(`[TaskExecutor] Failed to register metadata:`, error);
  }
}

元数据包含:

  • url: 图片/视频 URL
  • taskId: 任务 ID
  • model: 使用的模型(如 'imagen-3.0-generate-001'
  • prompt: 用户提示词
  • params: 完整的生成参数size, quality 等)
  • cachedAt: 缓存时间戳(自动添加)

数据库结构

IndexedDB: drawnix-unified-cache

Object Store: images

主键: url (string)

字段:

interface CachedImage {
  url: string;              // 图片 URL (主键)
  cachedAt: number;         // 缓存时间戳
  lastAccessed: number;     // 最后访问时间
  size: number;             // 文件大小(字节)
  mimeType: string;         // MIME 类型
  taskId?: string;          // 关联的任务 ID
  model?: string;           // 生成模型
  prompt?: string;          // 提示词
  params?: any;             // 生成参数
}

索引:

  • by-cachedAt: 按缓存时间排序
  • by-lastAccessed: 按访问时间排序(用于 LRU 清理)
  • by-taskId: 按任务 ID 查询

Object Store: migrations

用于记录数据迁移状态


PostMessage 通信协议

Service Worker → Main Thread

1. IMAGE_CACHED

图片已成功缓存

{
  type: 'IMAGE_CACHED',
  url: string,              // 图片 URL
  size: number,             // 文件大小
  mimeType: string,         // MIME 类型
  timestamp: number,        // 缓存时间
}

2. QUOTA_WARNING

存储配额警告≥95%

{
  type: 'QUOTA_WARNING',
  usage: number,            // 已用空间(字节)
  quota: number,            // 总配额(字节)
  percentage: number,       // 使用百分比
}

3. CACHE_DELETED

缓存已删除确认

{
  type: 'CACHE_DELETED',
  url: string,              // 删除的 URL
}

Main Thread → Service Worker

1. DELETE_CACHE

删除单个缓存

{
  type: 'DELETE_CACHE',
  url: string,              // 要删除的 URL
}

2. DELETE_CACHE_BATCH

批量删除缓存

{
  type: 'DELETE_CACHE_BATCH',
  urls: string[],           // URL 数组
}

3. CLEAR_ALL_CACHE

清空所有缓存

{
  type: 'CLEAR_ALL_CACHE',
}

文件清单

新建文件

  1. docs/UNIFIED_CACHE_DESIGN.md

    • 详细的设计文档
    • 架构图和数据流
    • API 规范
  2. packages/drawnix/src/services/unified-cache-service.ts

    • 核心缓存管理服务
    • 661 行代码
    • 单例模式
  3. packages/drawnix/src/hooks/useUnifiedCache.ts

    • React Hooks 封装
    • 3 个导出钩子
    • 172 行代码
  4. packages/drawnix/src/components/cache-quota-provider/CacheQuotaProvider.tsx

    • 全局配额监控组件
    • TDesign Dialog 集成
    • 60 行代码
  5. docs/UNIFIED_CACHE_TESTING.md

    • 完整测试清单
    • 14 个测试用例
    • 调试工具和脚本
  6. docs/UNIFIED_CACHE_IMPLEMENTATION_SUMMARY.md (本文件)

    • 实施总结文档

修改文件

  1. apps/web/src/sw/index.ts

    • 添加消息处理器DELETE_CACHE, DELETE_CACHE_BATCH, CLEAR_ALL_CACHE
    • 添加辅助函数deleteCacheByUrl, notifyImageCached, checkStorageQuota
    • 在缓存成功后调用通知函数
    • 约 200 行新增代码
  2. packages/drawnix/src/mcp/tools/canvas-insertion.ts

    • 修改 getImageDimensions() 函数
    • 使用 unifiedCacheService.getImageForAI() 替代直接使用 URL
    • 约 10 行修改
  3. packages/drawnix/src/drawnix.tsx

    • 导入 CacheQuotaProviderMediaLibraryModal
    • 添加 mediaLibraryOpen 状态
    • 创建 handleOpenMediaLibrary 回调
    • 包裹应用在 CacheQuotaProvider
    • 渲染 MediaLibraryModal
    • 约 20 行修改
  4. packages/drawnix/src/hooks/useTaskExecutor.ts

    • 导入 unifiedCacheService
    • 在任务完成时注册元数据2 处)
    • 约 30 行新增代码

配置常量

位置: packages/drawnix/src/services/unified-cache-service.ts

const CACHE_CONSTANTS = {
  DB_NAME: 'drawnix-unified-cache',
  DB_VERSION: 1,
  IMAGES_STORE: 'images',
  MIGRATIONS_STORE: 'migrations',

  DEFAULT_MAX_AGE: 24 * 60 * 60 * 1000,  // 24 小时
  MAX_IMAGE_SIZE: 3 * 1024 * 1024,       // 3MB
  DEFAULT_QUALITY: 0.8,                   // 压缩质量 80%

  QUOTA_WARNING_THRESHOLD: 0.95,          // 95% 配额警告

  LEGACY_DBS: ['aitu-media-cache', 'aitu-url-cache'],
  MIGRATION_KEY: 'CACHE_MIGRATION_DONE',
};

可根据需求调整:

  • DEFAULT_MAX_AGE: 修改 URL/Base64 切换阈值
  • MAX_IMAGE_SIZE: 修改压缩触发大小
  • DEFAULT_QUALITY: 修改压缩质量
  • QUOTA_WARNING_THRESHOLD: 修改配额警告阈值

向下兼容性

数据迁移

自动迁移旧版本的两个数据库:

  1. aitu-media-cache (旧版媒体缓存 - 包含元数据)
  2. aitu-url-cache (旧版 URL 缓存 - 基本信息)

迁移流程

应用启动
  ↓
检查 localStorage['drawnix_cache_migrated']
  ↓ 未标记
第一步: 迁移 aitu-media-cache
  - 读取所有数据(包含 taskId、prompt
  - 转换为新格式
  - 写入 drawnix-unified-cache
  ↓
第二步: 迁移 aitu-url-cache智能合并
  - 读取所有数据
  - 检查 URL 是否已存在
  - 已存在: 合并数据,保留元数据
  - 不存在: 创建新记录
  ↓
删除两个旧数据库
  ↓
设置迁移完成标记

智能合并逻辑

当同一张图片在两个旧数据库中都存在时:

// 检查是否已存在(来自 media-cache
const existing = await this.getItem(url);

if (existing) {
  // 智能合并:保留元数据,更新缺失字段
  const merged = {
    ...existing,  // 保留 taskId、prompt、model
    mimeType: existing.mimeType || item.mimeType,
    size: existing.size || item.size,
    cachedAt: Math.min(existing.cachedAt, item.cachedAt),  // 保留更早时间
  };
  await this.putItem(merged);
} else {
  // 创建新记录
  await this.putItem(newEntry);
}

迁移验证

使用 test-migration.js 验证迁移是否成功:

// 在浏览器控制台执行
const script = document.createElement('script');
script.src = '/test-migration.js';
document.head.appendChild(script);

// 自动验证:
// ✓ 迁移标记状态
// ✓ 旧数据库是否删除
// ✓ 新数据库数据完整性
// ✓ 元数据是否保留

// 手动操作:
cleanupOldDatabases()  // 清理旧数据库
forceMigration()       // 强制重新迁移

注意事项

  • ⚠️ 迁移只运行一次localStorage 标记)
  • ⚠️ 同一 URL 在两个库中存在时,元数据不会丢失
  • ⚠️ 迁移失败不会影响应用运行
  • ⚠️ 可以手动清理旧数据库
  • 迁移后旧数据库自动删除
  • 支持强制重新迁移

旧代码兼容

  • 旧的 mediaCacheServiceurlCacheService 可以继续使用
  • 但建议逐步迁移到 unifiedCacheService
  • 新功能(如智能传递、元数据)只在新服务中可用

性能优化

1. 缓存命中优先

Service Worker 拦截请求后优先返回缓存,避免不必要的网络请求。

2. 异步操作

所有 IndexedDB 操作都是异步的,不阻塞主线程。

3. 订阅模式

使用 RxJS Subject 实现发布-订阅,避免轮询。

4. 懒加载元数据

只在需要时才从 IndexedDB 查询详细信息。

5. 批量操作

支持批量删除缓存,减少事务开销。

6. 智能压缩

只在图片超过阈值时才压缩,避免不必要的计算。


安全性考虑

1. 同源策略

Service Worker 只缓存同源或启用 CORS 的图片。

2. HTTPS 要求

Service Worker 需要 HTTPS 环境(开发环境除外)。

3. 配额限制

浏览器会限制存储配额,系统会监控并提示用户。

4. 数据清理

用户可以通过媒体库手动清理缓存。

5. 错误处理

所有操作都有 try-catch 保护,不会导致应用崩溃。


监控和日志

控制台日志

Service Worker 日志

  • [SW Cache] Successfully cached image: <url>
  • [SW Cache] Notified main thread about cached image: <url>
  • [SW Cache] Storage quota: X.XX GB / X.XX GB (XX%)
  • [SW Cache] Warning: Storage quota exceeded 95%

主线程日志

  • [UnifiedCache] Registered metadata for: <url>
  • [UnifiedCache] Migration completed from <old-db>
  • [TaskExecutor] Registered metadata for task <id>
  • [TaskExecutor] Failed to register metadata: <error>

错误追踪

所有关键操作都有日志输出,便于调试:

  • 缓存操作成功/失败
  • 元数据注册成功/失败
  • 数据迁移进度
  • 配额监控状态

未来优化方向

1. LRU 自动清理

当缓存空间不足时,自动删除最近最少使用的图片。

实现思路:

  • 使用 lastAccessed 索引
  • 定期清理超过 N 天未访问的图片
  • 保留最近生成的图片

2. 缓存预热

预加载常用模型的样例图片,提升首次使用体验。

3. 差异化策略

  • 高质量模型图片保留更长时间
  • 测试图片优先清理
  • 用户收藏的图片永不清理

4. 压缩算法优化

  • 使用 WebP 格式替代 JPEG/PNG
  • 根据模型特点选择压缩参数
  • 支持有损和无损压缩切换

5. 统计分析

  • 缓存命中率统计
  • 存储空间使用趋势
  • 最常用模型分析
  • 用户行为洞察

6. 云端同步

  • 支持将缓存同步到云端
  • 跨设备共享缓存
  • 增量同步减少带宽

7. 用户设置

  • 允许配置缓存大小上限
  • 自定义清理策略
  • 选择是否启用自动缓存

8. 多格式支持

  • 支持视频缓存(目前主要是图片)
  • 支持音频缓存
  • 支持 3D 模型缓存

故障排查指南

问题 1: 图片没有被缓存

症状: 图片每次都从网络加载,没有命中缓存

可能原因:

  1. Service Worker 未正确注册
  2. 图片 URL 不符合缓存条件(非 HTTPS、CORS 问题)
  3. 缓存空间已满

排查步骤:

  1. 检查 chrome://serviceworker-internals/ 确认 SW 运行
  2. 查看 Network 标签页,确认响应头包含正确的 CORS 头
  3. 检查 IndexedDB 和 Cache Storage 的使用量
  4. 查看 Service Worker 控制台日志

解决方法:

  • 确保 Service Worker 正确注册并激活
  • 配置服务器返回正确的 CORS 头
  • 清理旧缓存释放空间

问题 2: 元数据没有注册

症状: IndexedDB 中没有图片元数据记录

可能原因:

  1. 任务没有正确完成status 不是 'completed'
  2. IndexedDB 权限被阻止
  3. useTaskExecutor 钩子没有加载

排查步骤:

  1. 查看任务队列中任务的 status
  2. 检查浏览器是否阻止了 IndexedDB
  3. 查看控制台是否有注册失败的错误
  4. 确认 useTaskExecutor 钩子在 Drawnix 组件中被调用

解决方法:

  • 等待任务完全完成
  • 检查浏览器隐私设置,允许 IndexedDB
  • 刷新页面重新加载钩子

问题 3: 缓存满对话框不显示

症状: 存储空间满时没有弹出提示对话框

可能原因:

  1. CacheQuotaProvider 未正确挂载
  2. useCacheQuotaMonitor 钩子没有订阅消息
  3. Service Worker 没有发送 QUOTA_WARNING 消息

排查步骤:

  1. 检查 Drawnix 组件树,确认 CacheQuotaProvider 存在
  2. 查看控制台是否有配额监控相关日志
  3. 手动触发配额警告测试
  4. 检查 Service Worker 是否正常运行

解决方法:

  • 确保 CacheQuotaProvider 正确包裹应用
  • 检查 onOpenMediaLibrary 回调是否正确传递
  • 测试手动触发配额警告

问题 4: 数据迁移失败

症状: 旧数据没有迁移到新数据库

可能原因:

  1. 旧数据库不存在或已被删除
  2. 迁移过程中出现错误
  3. 迁移标记已设置,跳过了迁移

排查步骤:

  1. 检查 chrome://indexeddb-internals/ 查看数据库列表
  2. 查看控制台迁移日志
  3. 检查 localStorage['CACHE_MIGRATION_DONE'] 标记
  4. 查看 IndexedDB 中新数据是否正确

解决方法:

  • 如果需要重新迁移,删除 localStorage 标记
  • 确保旧数据库存在且可访问
  • 查看错误日志定位具体问题

测试建议

详细的测试清单请参考 /docs/UNIFIED_CACHE_TESTING.md

快速冒烟测试

  1. 基础缓存测试:

    • 生成一张图片
    • 检查 Network 显示从 Service Worker 返回
    • 检查 IndexedDB 有元数据记录
  2. 智能传递测试:

    • 生成图片 A
    • 立即用 A 生成新图片 B应使用 URL
    • 修改 A 的缓存时间为 2 天前
    • 再用 A 生成图片 C应使用 base64
  3. 配额警告测试:

    navigator.serviceWorker.controller.postMessage({
      type: 'QUOTA_WARNING',
      usage: 4900000000,
      quota: 5000000000,
      percentage: 98
    });
    
    • 应显示对话框
  4. 媒体库集成测试:

    • 打开媒体库
    • 删除一张图片
    • 检查 IndexedDB 和 Cache Storage 都已清理

API 快速参考

UnifiedCacheService

// 注册元数据
await unifiedCacheService.registerImageMetadata(url, {
  taskId: 'task-123',
  model: 'imagen-3.0',
  prompt: 'A beautiful sunset',
  params: { size: '1024x1024' }
});

// 智能获取图片
const imageData = await unifiedCacheService.getImageForAI(url, {
  maxAge: 24 * 60 * 60 * 1000,  // 24 hours
  maxSize: 3 * 1024 * 1024,     // 3MB
  quality: 0.8,                  // 80%
});

// 获取缓存信息
const info = await unifiedCacheService.getCacheInfo(url);
// { isCached: true, cachedAt: 1234567890, size: 1024000, ... }

// 删除缓存
await unifiedCacheService.deleteCache(url);

// 获取统计
const usage = await unifiedCacheService.getStorageUsage();
// { usage: 1000000000, quota: 5000000000, percentage: 20 }

// 获取所有缓存 URL
const urls = await unifiedCacheService.getAllCachedUrls();

// 订阅变化
const unsubscribe = unifiedCacheService.subscribe(() => {
  console.log('Cache changed!');
});

React Hooks

// 单个图片管理
const { isCached, cacheImage, deleteCache } = useUnifiedCache(url);

// 整体统计
const { totalCount, storageUsage } = useCacheStats();

// 配额监控
const { isQuotaExceeded } = useCacheQuotaMonitor(onQuotaExceeded);

总结

本次实施完成了一个全面、健壮、易用的统一图片缓存系统,具有以下特点:

优势

双层存储架构,兼顾性能和灵活性 智能图片传递,优化 AI API 调用 完整的元数据记录,支持复杂查询 配额监控和用户提示,避免空间耗尽 自动数据迁移,向下兼容 React Hooks 封装,易于使用 全面的错误处理和日志 详细的文档和测试清单

技术亮点

🚀 Service Worker 拦截,零延迟响应 🚀 PostMessage 通信,松耦合架构 🚀 IndexedDB 索引,高效查询 🚀 RxJS 订阅模式,响应式更新 🚀 异步操作,不阻塞 UI 🚀 单例模式,统一管理

用户价值

💡 更快的图片加载速度 💡 智能的存储空间管理 💡 完整的生成历史追溯 💡 离线也能访问已缓存图片 💡 清晰的缓存使用提示


相关文档


实施完成日期: 2026-01-07 版本: 1.0.0 状态: 已完成并通过构建