如何使用滚动缓冲区附加到dexie条目(存储大型条目而不分配GB的内存)

时间:2017-05-06 18:59:57

标签: dexie

在通过电子邮件发送Dexie(David Fahlander)的作者后,我被重定向到这里。这是我的问题:

有没有办法追加现有的Dexie条目?我需要存储dexie中较大的东西,但我希望能够使用滚动缓冲区填充大型条目,而不是分配一个巨大的缓冲区然后进行存储。

例如,我有一个2gb文件,我想存放在dexie中。我希望通过一次将32kb存储到同一个商店来存储该文件,而无需在浏览器中分配2GB的内存。有没有办法做到这一点? put方法似乎只覆盖条目。

1 个答案:

答案 0 :(得分:1)

感谢您将问题放在stackoverflow :)这有助于我建立一个开放的知识库,供所有人访问。

IndexedDB中没有办法在没有实例化整个条目的情况下更新条目。 Dexie添加了update()和modify()方法,但它们只模拟了一种改变某些属性的方法。在后台,整个文档将始终暂时加载到内存中。

IndexedDB也支持Blob,但是当Blob i存储到IndexedDB中时,其整个内容将按规范克隆/复制到数据库中。

因此,处理此问题的最佳方法是为动态大型内容专门提供一个表格,并为其添加新条目。

例如,让我们说你有一个表"文件"和" fileChunks"。您需要逐渐增加"文件",每次执行此操作时,您都不希望将整个文件实例化到内存中。然后,您可以将文件块作为单独的条目添加到fileChunks表中。

let db = new Dexie('filedb');
db.version(1).stores({
    files: '++id, name',
    fileChunks: '++id, fileId'
});

/** Returns a Promise with ID of the created file */
function createFile (name) {
    return db.files.add({name});
}

/** Appends contents to the file */
function appendFileContent (fileId, contentToAppend) {
    return db.fileChunks.add ({fileId, chunk: contentToAppend});
}

/** Read entire file */
function readEntireFile (fileId) {
    return db.fileChunks.where('fileId').equals(fileId).toArray()
    .then(entries => {
        return entries.map(entry=>entry.chunk)
            .join(''); // join = Assume chunks are strings
    });
}

够容易。如果您希望appendFileContent成为滚动缓冲区(具有最大大小并删除旧内容),则可以添加truncate方法:

function deleteOldChunks (fileId, maxAllowedChunks) {
    return db.fileChunks.where('fileId').equals(fileId);
        .reverse() // Important, so that we delete old chunks
        .offset(maxAllowedChunks) // offset = skip
        .delete(); // Deletes all records older before N last records
}

您还可以获得其他好处,例如能够在不将整个内容加载到内存中的情况下拖尾存储文件:

/** Tail a file. This function only shows an example on how
 * dynamic the data is stored and that file tailing would be
 * simple to do. */
function tailFile (fileId, maxLines) {
    let result = [], numNewlines = 0;
    return db.fileChunks.where('fileId').equals(fileId)
        .reverse()
        .until(() => numNewLines >= maxLines)
        .each(entry => {
            result.unshift(entry.chunk);
            numNewlines += (entry.chunk.match(/\n/g) || []).length;
        })
    .then (()=> {
        let lines = result.join('').split('\n')
            .slice(1); // First line may be cut off
        let overflowLines = lines.length - maxLines;
        return (overflowLines > 0 ?
            lines.slice(overflowLines) :
            lines).join('\n');
    });
}

我知道在readEntireFile()和tailFile()中块将按正确的顺序排列的原因是,indexedDB查询将始终按查询列主列的顺序检索,但是按主键的顺序检索,这是自动递增的数字。

此模式可用于其他情况,例如日志记录等。如果文件不是基于字符串的,则必须稍微更改此示例。具体来说,不要使用string.join()或array.split()。