IndexedDB 实战:浏览器端大文件存储与离线架构

技术架构(更新于 2026年5月22日)

为什么需要 IndexedDB?

浏览器存储方案各有局限:

方案 容量 数据类型 同步 API 适用场景
Cookie ~4KB 字符串 会话标识
localStorage ~5MB 字符串 简单配置
sessionStorage ~5MB 字符串 临时状态
IndexedDB 数百 MB~GB 任意结构化数据 ❌ 异步 大文件、离线应用

对于需要在浏览器中缓存 PDF 处理中间结果、WASM 模块、或用户上传历史记录的工具站,IndexedDB 是唯一可行的选择。


核心概念

数据库 → 对象存储 → 记录

Database: "toolsku-cache"
  ├── ObjectStore: "wasm-modules"     (key: moduleName)
  ├── ObjectStore: "file-cache"       (key: fileHash, index: timestamp)
  └── ObjectStore: "user-preferences" (key: settingKey)

事务与并发

IndexedDB 的事务是自动提交的,且同一时刻每个 ObjectStore 只能有一个读写事务:

const tx = db.transaction(['file-cache'], 'readwrite');
const store = tx.objectStore('file-cache');
await store.put({ hash: 'abc123', data: arrayBuffer, timestamp: Date.now() });
await tx.done; // 等待事务完成

工具库中的 IndexedDB 应用

WASM 模块缓存

ffmpeg.wasm 约 30MB,每次加载耗时 3-5 秒。缓存到 IndexedDB 后,二次访问几乎零延迟:

async function getWasmModule(name: string): Promise<ArrayBuffer> {
  const cached = await idb.get('wasm-modules', name);
  if (cached) return cached;

  const response = await fetch(`/wasm/${name}.wasm`);
  const buffer = await response.arrayBuffer();
  await idb.put('wasm-modules', buffer, name);
  return buffer;
}

大文件分块存储

超过 50MB 的文件不适合一次性存入 IndexedDB。分块策略:

文件 (200MB) → 切分为 4MB chunks → 分别存入 IndexedDB
key: "file:{hash}:chunk:{index}"
读取时按序拼接 → 还原完整文件

版本迁移

数据库 schema 变更时需要 onupgradeneeded 处理迁移:

const request = indexedDB.open('toolsku-cache', 2); // 版本号 2

request.onupgradeneeded = (event) => {
  const db = event.target.result;
  if (event.oldVersion < 2) {
    db.createObjectStore('processing-history', { keyPath: 'id' });
  }
};

最佳实践:只增不减 ObjectStore,通过版本号递增触发迁移,避免删除用户数据。


IndexedDB vs OPFS

特性 IndexedDB OPFS (Origin Private File System)
API 成熟度 广泛支持 Chrome 86+ / Safari 15.2+
读写模式 键值存储 真正的文件系统
性能 中等 更高(尤其同步访问)
适用 结构化缓存 大文件读写

工具库目前以 IndexedDB 为主,OPFS 作为 Chrome 上的性能优化备选。


总结

IndexedDB 是构建离线优先浏览器工具的关键基础设施。合理设计 ObjectStore、利用 WASM 缓存、实施分块存储策略,可以让工具库在弱网环境下仍提供流畅体验。

本站提供浏览器本地工具,免注册即可试用 →

#IndexedDB#浏览器存储#离线#大文件#架构