检查 IndexedDB 数据库是否存在。

32

有没有一种方法可以检查 IndexedDB 数据库是否已经存在?当程序尝试打开一个不存在的数据库时,该数据库将被创建。 我能想到的唯一方法是类似以下代码,测试对象存储是否已经存在,如果不存在,则删除数据库:

var dbexists=false;
var request = window.indexedDB.open("TestDatabase");
request.onupgradeneeded = function(e) {
    db = e.target.result;
    if (!db.objectStoreNames.contains('todo')) {
       db.close();
       indexedDB.deleteDatabase("TestDatabase");
    } else {
       dbexists=true;
    }
}
10个回答

25

onupgradeneeded回调中,您可以检查版本号(e.target.result.oldversion)。如果它是0,则数据库不存在。

编辑: 经过一些调查,您无法100%确定是否创建了新数据库。我确定的一件事是,只有版本号为1或更高版本的indexeddb才能使用。我相信一个数据库可以存在并且具有版本0(唯一的事实是您无法使用它,onupgradeneeded事件将被调用)。

我已经构建了自己的indexeddbviewer。在那里,我打开没有版本号的indexeddb,如果我进入onupgradeneeded事件,则意味着数据库不存在。在这种情况下,我调用中止命令,以使其不升级到版本1。这是我检查它的方式。

var dbExists = true;
var request = window.indexeddb.open("db");
request.onupgradeneeded = function (e){
    e.target.transaction.abort();
    dbExists = false;
}

但是正如之前提到的,如果情况允许,数据库可能会继续存在,但是onupgradeneeded将会被始终调用。


1
这是个好主意。但如果我只想检查数据库是否存在,之后还得删除数据库。应该有一种方法来测试数据库是否存在。 - Pedro Almeida
1
在事务上调用abort方法。E.target.result.abort() - Kristof Degrave
1
我正在开发一个离线应用程序,如果存在本地数据库,则表示用户已经“加载”了应用程序并创建了数据库。如果本地数据库不存在,则必须登录到服务器以启动“安装”过程。 - Pedro Almeida
添加 e.target.transaction.onabort=function() {,它可以工作。感谢 Kristof 记住了这个可能性。 - Pedro Almeida
有关onupgradeneeded事件何时触发的更多信息,请参阅此文章:https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase.createObjectStore。只有在以前从未存在过数据库或用户指定与任何现有数据库不同的版本号时,它才会被触发。 - mattdlockyer
显示剩余8条评论

16

使用ES6,您可以通过以下代码按名称查找IndexedDB数据库:

const dbName = 'TestDatabase';
const isExisting = (await window.indexedDB.databases()).map(db => db.name).includes(dbName);


1
谢谢!这似乎是一种可靠的方法(至少在Chrome上),并且不会遇到OP所指出的问题,也不会像其他答案那样遇到问题(“当程序尝试打开不存在的数据库时,将创建该数据库”)。 (顺便说一句,我认为你的const isExiting应该是const isExisting ;) ) - Velojet
4
遗憾的是 indexedDB.databases 在 Firefox 中不可用 :( - icl7126

8
以下代码可以正常工作。我已经在Chrome、IE和Opera上进行了测试。使用本地开放的数据库和关闭的数据库以及不同版本的数据库进行了测试,因此应该很准确。需要创建/删除数据库。但是,这将是一个原子操作,没有竞态条件的风险,因为规范承诺如果打开请求导致数据库创建,则不会并行启动打开请求。
function databaseExists(dbname, callback) {
    var req = indexedDB.open(dbname);
    var existed = true;
    req.onsuccess = function () {
        req.result.close();
        if (!existed)
            indexedDB.deleteDatabase(dbname);
        callback(existed);
    }
    req.onupgradeneeded = function () {
        existed = false;
    }
}

要使用该函数,请执行以下操作:

databaseExists(dbName, function (yesno) {
    alert (dbName + " exists? " + yesno);
});

7
什么?如果数据库已经存在,为什么要删除它?我们正在测试它是否存在,如果存在,我们不希望删除它。难道不应该是 if (!existed) indexedDB.deleteDatabase(dbname); 吗? - lisak

5
我花了一个多小时来测试它,基本上唯一确定和可靠的方法是使用Webkit的 webkitGetDatabaseNames。实际上有大约10种方法可以使用onupgradeneeded来测试数据库是否存在,但那在生产环境中并不起作用。在删除数据库时,它有时会被完全阻塞几秒钟。那些关于中止事务的提示都是无稽之谈,因为window.indexeddb.open("db")请求没有包含事务对象...req.transaction == null。我简直不敢相信这是真的...

该请求在“upgradeneeded”事件的处理程序中确实具有事务。 - Pixievolt No. 1

2

此函数用于检查数据库是否存在。如果版本为1且触发了onupgradeneeded事件,则表示数据库不存在,但是会使用window.indexedDB.open(name)函数创建它,因此您应该将其删除。

当onsuccess事件触发但未触发onupgradeneeded事件(变量dbExists仍为true)时,表示该数据库之前已经存在并返回true。

/**
 * Check if a database exists
 * @param {string} name Database name
 * @param {function} callback Function to return the response
 * @returns {bool} True if the database exists
 */
function databaseExists(name, callback) {
  var dbExists = true;
  var request = window.indexedDB.open(name);
  request.onupgradeneeded = function (e) {
    if (request.result.version === 1) {
      dbExists = false;
      window.indexedDB.deleteDatabase(name);
      if (callback) callback(dbExists);
    }
  };
  request.onsuccess = function (e) {
    if (dbExists) {
      if (callback) callback(dbExists);
    }
  };
}

该函数的输出通过回调函数实现。使用形式如下:
var name = "TestDatabase";

databaseExists(name, function (exists) {
  if (exists) {
    console.debug("database " + name + " exists");
  } else {
    console.debug("database " + name + " does not exists");
  }
});

[抱歉我的英文不好]


2
这是一个异步工具函数,用于检查给定名称和版本的数据库是否存在,并返回true或false。

const indexedDBUtil = {
    dbExist: async(dbName: string, version = 1)=> {
        let newDb = false;
        await ((): Promise<void>=>{
            return new Promise((resolve, reject)=>{
                const req = indexedDB.open(dbName, version );
                req.onupgradeneeded = ()=>{
                    req.transaction.abort();
                    newDb = true;
                    resolve();
                }
                req.onsuccess = () => {
                    resolve();
                }
            });
        })();
        
        return newDb;
    }
}


2
function databaseExists(name) {
  return new Promise(function (resolve, reject) {
    var db = indexedDB,
      req;

    try {
      // See if it exist
      req = db.webkitGetDatabaseNames();
      req.onsuccess = function (evt) {
        ~[].slice.call(evt.target.result).indexOf(name)
          ? resolve(true)
          : reject(false);
      };
    } catch (e) {
      // Try if it exist
      req = db.open(name);
      req.onsuccess = function () {
        req.result.close();
        resolve(true);
      };
      req.onupgradeneeded = function (evt) {
        evt.target.transaction.abort();
        reject(false);
      };
    }
  });
}

使用方法:

databaseExists("foo").then(AlreadyTaken, createDatabase)

webkitGetDatabaseNames 已在 Chrome 60 中被弃用。 - xkeshav
是的,webkitGetDatabaseNames现在不能使用。但是请参见Benny Neugebauer下面的答案以获取其替代方法。 - Velojet

1

如果您正在使用alasql,您可以使用类似以下的代码:

async existsDatabase(myDatabase) {
    return !(await alasql.promise(`
        create indexeddb database if not exists ${myDatabase};
    `));
}

这将创建数据库,如果它不存在,但这是我到目前为止找到的最好的解决方案。您也可以使用类似的查询删除数据库,如果它存在:drop indexeddb database if exists ${myDatabase};


0

另一种在Chrome上(但不适用于Firefox)实现此操作的方法是使用异步函数,如下所示:

/**
 * Checks the IndexedDB "web-server" to see if an specific database exists.
 * Must be called with await, for example, var dbFound = await doesDbExist('mySuperDB');
 * @param {string} dbName The database name to look for.
 * @returns {boolean} Whether a database name was found.
 */
async function doesDbExist(dbName) {
    var result = await indexedDB.databases();
    var dbFound = false;
    for (var i = 0; i < result.length && !dbFound; i++) {
        dbFound = result[i].name === dbName;
    }
    return dbFound;
}

然后只需按以下方式调用函数:

var dbFound = await doesDbExist('mySuperDB');

在Firefox上,indexedDB.databases不是一个函数。 - Michael Cole
抱歉,我应该明确指出这是针对 Chrome 的。 - Hector Diaz Contreras
你能告诉我在哪里可以找到支持“数据库”功能的浏览器吗?我在 caniuse.com 上没有找到这种细节级别的信息。 - Michael
1
你可以在浏览器兼容性中找到相关信息。@Michael - Hector Diaz Contreras

-1

你好,我知道这个问题已经被回答并且被接受了,但是我认为这是一种不错的解决方法

var indexeddbReq = $window.indexedDB.webkitGetDatabaseNames();
                indexeddbReq.onsuccess = function(evt){
                    if(evt.target.result.contains(
                       // SUCCESS YOU FOUND THE DB
                    }
                    else{
                       // DB NOT FOUND
                    }
                }

请纠正格式错误的代码,然后我会再次点赞。 - Stefan Sprenger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接