You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

IndexedDB间歇性无法添加图片文件问题排查

解决IndexedDB结合Service Worker后台同步时间歇性无法存储图片的问题

首先咱们拆解下你遇到的核心问题:在移动端Chrome 66环境下,用IndexedDB配合Service Worker做后台同步时,间歇性出现图片无法存入数据库的情况,没有报错但会触发data may be stale警告。结合你提供的代码,我梳理了几个关键问题和对应的修复方案:

1. 重复打开IndexedDB连接的竞态冲突

你在putScreenshotInDb函数里又重新调用了indexedDB.open,这会创建新的数据库连接,和之前的连接容易产生竞态冲突——尤其是后台同步这种异步场景下,很可能导致事务状态异常,触发data may be stale警告。

修复方案:复用已有的数据库连接,不要每次存储都重新打开。把db变量作为可复用实例,统一管理连接生命周期:

function storetoIndexeddb(id, file) {
  var docID = $scope[id];
  var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB;
  var dbVersion = 1;
  var dbName = "testDb";
  var db;

  if (!indexedDB) {
    alert("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.");
    return;
  }

  // 仅打开一次数据库连接
  var request = indexedDB.open(dbName, dbVersion);

  request.onerror = function(event) {
    console.error("Error creating/accessing IndexedDB database:", event);
  };

  // 完全依赖标准的版本升级事件创建对象仓库
  request.onupgradeneeded = function(event) {
    console.log("Upgrading IndexedDB database");
    db = event.target.result;
    // 避免重复创建对象仓库报错
    if (!db.objectStoreNames.contains("saveData")) {
      db.createObjectStore("saveData");
    }
  };

  request.onsuccess = function(event) {
    console.log("Success creating/accessing IndexedDB database");
    db = event.target.result;
    putScreenshotInDb();
  };

  function putScreenshotInDb() {
    // 使用已有的db连接创建事务
    const transaction = db.transaction(["saveData"], "readwrite");
    const objectStore = transaction.objectStore("saveData");
    
    const key = $routeParams.serviceRequestId + 'XXX' + $scope.inspectData.assetId + "XXX" + docID + 'XXXinspImage';
    const putReq = objectStore.put(file, key);

    // 确保数据写入成功后再更新UI
    putReq.onsuccess = function() {
      console.log("Image stored successfully");
      $scope.imageStorageRef[id].uploaded = true;
      $scope.loading = false;
      // AngularJS异步回调需手动触发digest更新视图
      if (!$scope.$$phase) $scope.$apply();
    };

    putReq.onerror = function(event) {
      console.error("Failed to store image:", event);
      $scope.loading = false;
      if (!$scope.$$phase) $scope.$apply();
    };

    transaction.oncomplete = function(event) {
      console.log("Transaction completed:", event);
    };

    transaction.onerror = function(event) {
      console.error("Transaction error:", event);
    };
  }
}

2. 废弃API导致的版本管理混乱

你代码里的setVersion是非常老旧的兼容写法,Chrome 66已经完全支持标准的onupgradeneeded事件,继续使用setVersion会导致数据库版本管理混乱,进而引发数据写入异常。

修复方案:彻底移除所有setVersion相关代码,完全依赖onupgradeneeded事件处理数据库版本升级和对象仓库创建,这也是IndexedDB标准推荐的做法。

3. UI状态更新与事务不同步

你之前在调用put之后立刻更新了$scope状态,但此时事务可能还未完成——如果中途出现data may be stale这类异常,就会导致UI状态和实际数据状态不一致。

修复方案:把UI状态更新逻辑放在putReq.onsuccesstransaction.oncomplete回调里,确保数据确实写入成功后再更新视图,同时记得手动触发AngularJS的$apply()(因为IndexedDB回调不在Angular的digest周期内)。

4. 后台同步的事务并发问题

后台同步时,Service Worker和页面线程可能同时操作IndexedDB,如果没有处理好事务并发,就会触发data may be stale警告(通常是一个事务尝试修改另一个事务已锁定的数据)。

修复方案:添加事务重试逻辑,当遇到data may be stale或事务失效错误时,自动重试操作:

function retryTransaction(db, storeName, operation, maxRetries = 3) {
  return new Promise((resolve, reject) => {
    function attempt(retryCount) {
      const transaction = db.transaction([storeName], "readwrite");
      const store = transaction.objectStore(storeName);
      const request = operation(store);

      request.onsuccess = () => resolve(request.result);
      request.onerror = (event) => {
        const error = event.target.error;
        // 匹配事务失效或data may be stale相关错误
        if (error.name === 'TransactionInactiveError' || error.message.includes('data may be stale')) {
          if (retryCount < maxRetries) {
            setTimeout(() => attempt(retryCount + 1), 100);
          } else {
            reject(error);
          }
        } else {
          reject(error);
        }
      };
    }
    attempt(0);
  });
}

// 使用示例
const key = $routeParams.serviceRequestId + 'XXX' + $scope.inspectData.assetId + "XXX" + docID + 'XXXinspImage';
retryTransaction(db, "saveData", (store) => store.put(file, key))
  .then(() => {
    console.log("Image stored successfully");
    $scope.imageStorageRef[id].uploaded = true;
    $scope.loading = false;
    if (!$scope.$$phase) $scope.$apply();
  })
  .catch((err) => {
    console.error("Failed to store image after retries:", err);
    $scope.loading = false;
    if (!$scope.$$phase) $scope.$apply();
  });

关于data may be stale警告的本质

这个警告通常意味着IndexedDB事务的状态已失效,常见原因包括:

  • 事务被意外中止;
  • 多个连接/事务同时操作同一数据;
  • 数据库版本变更导致旧事务失效;
  • 在事务完成前关闭了数据库连接。

通过上面的修复,应该能解决你遇到的间歇性存储失败问题,尤其是在Chrome 66这种对IndexedDB标准支持更严格的版本里。

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

火山引擎 最新活动