Предотвращение дублирования объектов 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
Кроме того, в вашем примере кода вы не ссылаетесь на таблицу 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-объектами). Однако я бы никогда не рекомендовал индексировать такое большое значение в любой базе данных. Еще лучше проиндексировать дайджест.