Firefox中带autoIncrement的IndexedDB存储Blob无响应问题
Firefox下IndexedDB存储Blob(autoIncrement模式)静默失败且阻塞数据库的问题
我用IndexedDB实现长媒体录制的Blob分片存储功能,代码在Chrome和Edge中运行正常,但Firefox中出现异常:数据库初始连接成功,但后续的存储请求完全无反应,没有任何报错信息,事务看起来被阻塞。
经过排查,问题明确指向使用autoIncrement+keyPath配置直接存储Blob实例的场景:
- 手动为Blob包装对象分配自增ID可正常工作
- 存储普通无ID对象(依赖autoIncrement)也可正常工作
- 直接存Blob不仅会静默失败,还会导致数据库后续请求彻底失效,甚至页面重载后也无法恢复
以下是简化后的核心代码:
const dbName = 'recording' const storeValue = 'blobs' let connection = null const handler = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB function connect() { return new Promise((resolve, reject) => { const request = handler.open(dbName) request.onupgradeneeded = (event) => { const db = event.target.result if (db.objectStoreNames.contains(storeValue)) { db.deleteObjectStore(storeValue) } db.createObjectStore(storeValue, { keyPath: 'id', autoIncrement: true, }) } request.onerror = () => reject() request.onsuccess = () => { connection = request.result connection.onerror = () => (connection = null) connection.onclose = () => (connection = null) resolve() } }) } async function saveChunk(chunk) { if (!connection) await connect() return new Promise((resolve, reject) => { const store = connection.transaction(storeValue, 'readwrite').objectStore(storeValue) const req = store.add(chunk) req.onsuccess = () => { console.warn('DONE!') // Chrome/Edge触发,Firefox不触发 resolve(req.result) } req.onerror = () => reject() req.transaction.oncomplete = () => console.warn('DONE!') // Chrome/Edge触发,Firefox不触发 }) } // 使用方式 // await saveChunk(blob)
已尝试的排查步骤:
- 关闭其他浏览器窗口,排除连接阻塞
- 重置Firefox配置文件
- 多设备测试,问题一致
补充细节:
- 代码运行在Nuxt 2.15.8开发环境(localhost:3000),作为组件Mixin使用,项目中仅此处使用IndexedDB
- 新建数据库后短时间内事务可正常完成,但后续请求会无限排队;页面重载后初始请求也会失效
解决方案
方案1:将Blob包装为普通JS对象存储
不要直接存储Blob实例,而是将其放入一个普通对象中,保留autoIncrement和keyPath配置,Firefox可正常处理自动ID生成:
// 调用时 await saveChunk({ data: blob }) // 数据库结构无需修改,仍保持keyPath: 'id'和autoIncrement: true
方案2:手动维护自增ID
如果需要直接关联ID与Blob(不包装对象),可手动查询当前最大ID并赋值(注意:Blob本身不可添加属性,仍需包装):
async function getNextId() { if (!connection) await connect() return new Promise((resolve) => { const store = connection.transaction(storeValue, 'readonly').objectStore(storeValue) const req = store.openCursor(null, 'prev') // 查询最后一条记录 req.onsuccess = () => { const cursor = req.result resolve(cursor ? cursor.id + 1 : 1) } }) } // 修改saveChunk async function saveChunk(blob) { const id = await getNextId() if (!connection) await connect() return new Promise((resolve, reject) => { const store = connection.transaction(storeValue, 'readwrite').objectStore(storeValue) const req = store.add({ id, data: blob }) // 包装为带ID的对象 req.onsuccess = () => resolve(req.result) req.onerror = () => reject() }) }
原因分析
这是Firefox对IndexedDB的一个兼容性问题:当直接存储Blob类型对象且同时使用autoIncrement+keyPath配置时,Firefox无法正确完成自动ID的赋值与事务提交,导致事务静默挂起,进而阻塞后续所有数据库请求,甚至损坏数据库连接状态。而Chrome/Edge对这种场景的处理更为宽松,可正常完成自动ID生成与存储。
内容的提问来源于stack exchange,提问作者Martin Diehl
相关产品推荐
相关产品推荐

