Предотвращение дублирования объектов Blob в IndexedDB

Есть ли встроенное решение для предотвращения дублирования Blob объекты в разных записях в IndexedDB?

Скажем, у меня есть эта схема для музыкального магазина: id, title, album, artworkи я хотел добавить в этот магазин 2 песни из одного альбома (поэтому они, скорее всего, имеют одинаковые художественные объекты). Существует ли встроенный способ автоматического сохранения художественного произведения только один раз?

Что я пробовал:

  • Я пытался установить unique флаг в artwork индекс, но после проверки размера базы данных до и после вставки второй песни (используя chrome://settings/cookies), произведение искусства хранится дважды.

  • Затем я попытался сохранить произведения искусства в отдельном магазине (только с id а также artwork как схема) с тем же флагом, но это тоже не сработало.


var database = new Dexie('TheDatabase');
database.version(1).stores({artworks: '++id, &artwork'});

var dbArt = database.artworks;

var artworkBlob = getBlob(image);
dbArt.put(artworkBlob);

//later:
dbArt.put(artworkBlob);

Я неправильно использую unique флаг каким-либо образом? Разве это не поддерживается в Blob объекты?

1 ответ

Решение

Несмотря на то, что IndexedDB поддерживает хранение BLOB-объектов, он не поддерживает индексацию BLOB-объектов. Индексируемые свойства могут иметь только тип string, number, Date или Array. Если нет, IndexedDB просто игнорирует, чтобы проиндексировать этот конкретный объект.

Кроме того, в вашем примере кода вы не ссылаетесь на таблицу artworks, и вы пытаетесь поместить сам блоб вместо того, чтобы помещать документ, содержащий свойство blob.

Поэтому вместо этого вы можете вычислить хэш / дайджест содержимого блоба и сохранить его в виде строки, которую вы можете индексировать, используя уникальный индекс.

var dbArt = new Dexie('TheDatabase');
dbArt.version(1).stores({
    artworks: `
        ++id,
        title,
        album,
        &artworkDigest` // & = unique index of the digest
});

var artworkBlob = getBlob(image); // get it somehow...

// Now, compute hash before trying to put blob into DB
computeHash(artworkBlob).then(artworkDigest => {

    // Put the blob along with it's uniqely indexed digest
    return dbArt.artworks.put({
        title: theTitle,
        album: theAlbum,
        artwork: artworkBlob,
        artworkDigest: artworkDigest
    });
}).then(()=>{
    console.log("Successfully stored the blob");
}).catch(error => {
    // Second time you try to store the same blob, you'll
    // end up here with a 'ConstraintError' since the digest
    // will be same and conflict the uniqueness constraint.
    console.error(`Failed to store the blob: ${error}`);
});

function computeHash (blob) {
    return new Promise((resolve, reject) => {
        // Convert to ArrayBuffer
        var fileReader = new FileReader();
        fileReader.onload = () => resolve(filerReader.result);
        fileReader.onerror = () => reject(filerReader.error);
        fileReader.readAsArrayBuffer(blob);
    }).then (arrayBuffer => {
        // Compute Digest
        return crypto.subtle.digest("SHA-256", arrayBuffer);
    }).then (digest => {
        // Convert ArrayBuffer to string (to make it indexable)
        return String.fromCharCode.apply(
            null, new Uint8Array(digest));
    });
};

Я бы также рекомендовал хранить ArrayBuffer, а не BLOB-объект (поскольку мы в любом случае читаем BLOB-объект в ArrayBuffer. Мой пример этого не показывает, но вы можете разбить computeHash() на две разные функции - одну, которая считывает BLOB-объект в ArrayBuffer и другой, который его хэширует. Это будет менее подвержено ошибкам в Safari и некоторых других более старых версиях Firefox, если вы сохраните ArrayBuffer вместо BLOB-объекта).

Примечание: в IndexedDB 2.0 ArrayBuffers индексируются (но все еще не являются BLOB-объектами). Однако я бы никогда не рекомендовал индексировать такое большое значение в любой базе данных. Еще лучше проиндексировать дайджест.

Другие вопросы по тегам