# 实现方案 — 2026-04-16-20-46-50 ## 根因分析 ### 1. LocalStorage 容量超限(QuotaExceededError) 浏览器对单个域名的 `localStorage` 通常有 **5MB** 的严格容量限制。 当前代码在抽帧时使用了视频的原始分辨率: ```tsx canvas.width = video.videoWidth; canvas.height = video.videoHeight; const dataUrl = canvas.toDataURL('image/jpeg', 0.9); ``` 如果上传的是 1080p 甚至 4K 视频: - 单张 0.9 质量 JPEG Base64 图片可能达到 **300KB ~ 1MB**; - 自动提取 12 张关键帧 + 手动截图若干张,总数据量可能达到 **5MB ~ 10MB**; - 直接超过 `localStorage` 的 5MB 上限。 ### 2. 静默失败 `src/utils/storage.ts` 中的 `set` 方法: ```typescript set(key: string, value: T): void { try { localStorage.setItem(key, JSON.stringify(value)); } catch { // ignore quota exceeded } } ``` 当数据量超过 5MB 时,`localStorage.setItem` 抛出 `QuotaExceededError`,但被 `catch` 静默吞掉。 **实际发生的过程:** 1. 用户上传视频 → 此时 `videos` 数组中的 `url` 是 `blob:http://...`(短字符串),数据量很小,**保存成功**; 2. 系统开始自动抽帧,生成巨大的 Base64 `dataUrl` 数组; 3. 调用 `saveDraftToStorage()` 尝试保存时,`localStorage.setItem` 触发超限报错; 4. 异常被 `catch` 忽略,**draft 没有被更新**(或更新失败); 5. 当用户离开页面再返回时,localStorage 中读到的 draft 仍然停留在"仅有视频、没有关键帧"的状态。 这就是为什么:编辑器内容保留了,视频保留了,但**关键帧全部消失**。 ## 修改方向 ### 方向一:压缩关键帧分辨率与质量(快速修复,推荐优先执行) 关键帧只是用于插入报告的缩略图,通常不需要 4K 原画质。可以: 1. 设定最大宽度(如 800px),等比缩放 Canvas; 2. 将 JPEG 导出质量从 `0.9` 降到 `0.5 ~ 0.6`; 3. 这样单张图片体积可从 500KB 压缩到 30KB~80KB,十几张关键帧总计不到 1MB,远低于 5MB 限制。 **修改点:** - `captureFrame()`(手动截图) - `autoCaptureFrames()`(自动抽帧) ### 方向二:增加存储超限的可见性 在 `storage.ts` 中不再静默吞掉异常,而是至少输出 `console.error`,甚至可以在 UI 层捕获后提示用户: "报告数据过大,请降低视频截图质量或删除部分图片。" ### 方向三:迁移到 IndexedDB(长期根治) `localStorage` 的 5MB 上限对于包含大量 Base64 图片的医疗报告系统来说迟早会不够用。长期方案是: - 引入 `localforage` 或 `idb-keyval` 等轻量库; - 将 `storage.ts` 改造为基于 IndexedDB 的异步存储方案(容量可达数百 MB)。 **注意:** 方向三涉及全项目的 `storage.get/set` 调用点从同步改为异步,改动面较大,适合作为后续迭代项目。 ## 建议的实施方案 **本次优先执行方向一 + 方向二**,以最快速度解决关键帧丢失问题,并让用户感知到存储异常: 1. 在 `captureFrame` 和 `autoCaptureFrames` 中增加 Canvas 等比缩放逻辑: ```tsx const MAX_WIDTH = 800; const scale = Math.min(1, MAX_WIDTH / video.videoWidth); canvas.width = video.videoWidth * scale; canvas.height = video.videoHeight * scale; ctx?.drawImage(video, 0, 0, canvas.width, canvas.height); const dataUrl = canvas.toDataURL('image/jpeg', 0.6); ``` 2. 在 `storage.ts` 中增加超限日志: ```tsx } catch (e) { console.error('Storage save failed (possibly quota exceeded):', e); } ``` ## 风险点 | 风险 | 级别 | 应对措施 | |------|------|---------| | 压缩后图片清晰度下降 | 低 | 800px 宽度 + 0.6 质量对于报告插入足够清晰 | | 仍有个别超长视频压缩后接近 5MB | 极低 | 配合方向二的日志提示,便于后续继续优化 | ## 回滚策略 仅调整 Canvas 缩放参数和 JPEG 质量,不涉及数据结构和接口变更。如有异常可直接 revert。 --- **⚠️ 请审核以上方案,确认无误后回复「确认」或提出修改意见,我将进入测试方案编写阶段。**