You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用IndexedDB存储通过fetch获取的文件?解决事务失效报错

如何用IndexedDB存储通过fetch获取的文件?

需要下载一组文件并使用IndexedDB进行本地存储,当前使用fetch API的代码如下:

cacheRecordings() {
      var request = window.indexedDB.open("database", 2);

      request.onsuccess = event => {
          var database = event.target.result;
          var transaction = database.transaction(["store"], 'readwrite'); //second step is opening the object store
          this.objectStore = transaction.objectStore("store");      
      }

      for (const url of this.urls) {
        fetch(url)
          .then(resp => resp.blob())
          .then(blob => {
            const url = window.URL.createObjectURL(blob);
            const index = this.objectStore.index('path');
            index.openCursor().onsuccess = function(event) { <-- Error is thrown here
              this.objectStore.add(url, path);
            }.bind(this)
          })
          .catch((error) => {
            console.log(error)
          })
      } 
}

执行代码时出现以下错误:

Failed to execute 'openCursor' on 'IDBIndex': The transaction is not active.

Failed to execute 'index' on 'IDBObjectStore': The transaction has finished

曾找到相关问题,但不适用于fetch获取文件的场景:《TransactionInactiveError: 无法在IDBObjectStore上执行'add':事务未激活》


问题原因

IndexedDB的事务具有自动提交机制:当事务作用域内没有待处理的数据库请求时,事务会立即关闭。原代码中,request.onsuccess回调执行完毕后,事务就自动提交了;而fetch是异步操作,等fetch完成后再去操作objectStore时,事务已经处于非活跃状态,因此抛出错误。

另外,代码中使用window.URL.createObjectURL(blob)存储临时URL是不合理的——该URL仅在当前页面会话有效,页面刷新后就会失效,无法长期存储文件内容。

解决方案

将所有异步操作(fetch+数据库存储)放在事务的作用域内,使用async/await或Promise管理异步流程,确保事务在所有操作完成前保持活跃。同时直接存储Blob对象而非临时URL,保证文件可长期读取。

修正后的代码示例:

cacheRecordings() {
  const request = window.indexedDB.open("database", 2);

  request.onsuccess = async (event) => {
    const db = event.target.result;
    // 创建读写事务,作用域覆盖所有文件处理逻辑
    const transaction = db.transaction(["store"], 'readwrite');
    const objectStore = transaction.objectStore("store");

    // 监听事务全局状态
    transaction.oncomplete = () => {
      console.log("所有文件已成功存储到IndexedDB");
    };

    transaction.onerror = (err) => {
      console.error("事务执行失败:", err);
    };

    try {
      // 并行处理所有文件的下载与存储
      await Promise.all(this.urls.map(async (fileUrl) => {
        const resp = await fetch(fileUrl);
        const blob = await resp.blob();
        // 用文件URL的路径作为主键(可根据需求自定义唯一标识)
        const fileKey = new URL(fileUrl).pathname;

        // 先检查文件是否已存在,避免重复存储
        const existingFile = await objectStore.get(fileKey);
        if (!existingFile) {
          await objectStore.add(blob, fileKey);
        } else {
          console.log(`文件 ${fileKey} 已存在,跳过存储`);
        }
      }));
    } catch (err) {
      console.error("文件处理出错:", err);
      // 发生异常时中止事务,回滚所有操作
      transaction.abort();
    }
  };

  request.onerror = (err) => {
    console.error("打开数据库失败:", err);
  };
}

关键优化点

  1. 事务作用域覆盖异步逻辑:将fetch和数据库操作全部放在request.onsuccess回调内部的事务作用域中,确保事务在所有异步操作完成前不会自动提交。
  2. 直接存储Blob:Blob是文件的二进制对象,可长期保存在IndexedDB中,避免临时URL失效问题。
  3. 高效的存在性检查:使用objectStore.get()代替游标查询,直接通过主键检查文件是否已存在,提升性能。
  4. 并行处理与错误控制:用Promise.all并行处理多个文件的下载和存储,同时通过try/catch捕获异常,必要时中止事务回滚操作。
  5. 事务状态监听:通过transaction.oncomplete和transaction.onerror监听事务的全局完成/失败状态,便于统一处理结果。

内容的提问来源于stack exchange,提问作者etayluz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 04:18:16