IndexedDB可直接存储Blob和ArrayBuffer,推荐分块上传+流式读取+合理索引;单Blob建议≤200MB,超大文件需分片存于file_chunks并用files存元数据;读取时优先用stream避免内存爆炸。

IndexedDB 可以直接存储 Blob 和 ArrayBuffer 类型的数据,包括图片、音频、视频等大体积二进制内容。关键不是“能不能存”,而是“怎么存得稳、读得快、不卡顿、不爆内存”。核心策略是:分块上传 + 流式写入(可选)+ 合理索引 + 限制单条记录大小(避免触发浏览器内部限制)。
用 Blob 直接存,但注意 size 限制和事务生命周期
现代浏览器(Chrome/Firefox/Safari 15.4+)支持将 Blob 直接作为 value 存入 IndexedDB 对象存储(Object Store)。无需转 base64 或 typed array —— 转 base64 反而会增大 33% 体积且消耗 CPU。
- 确保数据库版本升级时,对象存储的 autoIncrement 和 keyPath 配置兼容 Blob 数据(通常用自增 ID 或文件哈希作 key)
- 写入大 Blob 时,必须在 readwrite 事务中完成,且不能让事务长时间挂起;建议单个事务只写 1–3 条大记录,避免因超时被中止
- Chrome 对单条记录有隐式限制(约 500MB),虽无硬报错,但接近时可能 OOM 或写入失败;建议单个 Blob 控制在 200MB 内,超大文件走分片
超大文件(>200MB)推荐分片存储 + 元数据管理
把一个大图片或视频拆成固定大小的 ArrayBuffer 片段(如 4MB/片),每片单独存为一条记录,并用一个主记录保存元信息(原始文件名、总片数、MIME 类型、各片 key 等)。
- 定义两个 objectStore:files(存元数据,key 是 fileID)和 file_chunks(存分片,key 格式为
"${fileID}_${index}") - 读取时先 get files[fileID],再用 ID 批量 openCursor 或 getKeyRange 查询所有分片,按 index 拼接成完整 Blob
- 利用 IDBKeyRange.bound 高效查询某文件所有分片:
IDBKeyRange.bound(`${fileID}_0`, `${fileID}_99999`)
读取 Blob 时避免内存爆炸:用 stream 方式或分片加载
直接 cursor.value.arrayBuffer() 会把整个 Blob 加载进内存。对 >50MB 的图片,易触发内存警告甚至页面崩溃。
- 优先使用
cursor.value.stream()(返回 ReadableStream),配合response.body = stream直接传给或下载链路 - 若需局部处理(如生成缩略图),可用
slice(start, end)提取部分 ArrayBuffer,再用createObjectURL(new Blob([chunk]))创建临时 URL - 不要在主线程一次性调用
blob.text()或blob.arrayBuffer()处理整图 —— 改用 Web Worker + Transferable 分片处理
实用小技巧:压缩前预检 + 存储后校验
用户上传前可快速判断是否值得存:用 blob.size 和 blob.type 做初步过滤;存完后建议用 SHA-256(Web Crypto API)算哈希存入元数据,下次读取时比对,防止磁盘损坏或写入不完整。
- 用
navigator.storage.estimate()获取当前配额与已用空间,接近 80% 时提示用户清理或拒绝新上传 - 删除大 Blob 时,记得同步删掉所有关联分片(用 compound key 或 prefix 查询),否则产生“幽灵碎片”占用空间
- 开发期用 Chrome DevTools → Application → Storage → IndexedDB 查看实际存储结构和大小,验证分片逻辑是否生效










