IndexedDB间歇性无法添加图片文件问题排查
首先咱们拆解下你遇到的核心问题:在移动端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.onsuccess或transaction.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




