Javascript 中的大型 blob 文件

Large blob file in Javascript

我有一个 XHR 对象可以下载 1GB 的文件。

function getFile(callback)
{
    var xhr = new XMLHttpRequest();
    xhr.onload = function () {
        if (xhr.status == 200) {
            callback.apply(xhr);
        }else{
            console.log("Request error: " + xhr.statusText);
        }
    };

    xhr.open('GET', 'download', true);
    xhr.onprogress = updateProgress;
    xhr.responseType = "arraybuffer";
    xhr.send();
}

但文件 API 无法将所有内容加载到内存中,即使是从 worker 它抛出内存...

btn.addEventListener('click', function() {
    getFile(function() {
        var worker = new Worker("js/saving.worker.js");
        worker.onmessage = function(e) {
            saveAs(e.data); // FileSaver.js it creates URL from blob... but its too large
        };

        worker.postMessage(this.response);
    });
});

网络工作者

onmessage = function (e) {
    var view  = new DataView(e.data, 0);
    var file = new File([view], 'file.zip', {type: "application/zip"});
    postMessage('file');
};

我不是要压缩文件,此文件已从服务器压缩。

我想先将它存储在 indexedDB 上,但无论如何我都必须加载 blob 或文件,即使我按范围字节请求,迟早我将不得不构建这个巨大的 blob..

我想创建 blob:url 并在浏览器下载后发送给用户

我将为 Google Chrome 使用文件系统 API,但我想为 firefox 制作一些东西,我查看了文件句柄 Api 但没有...

我是否必须为 firefox 构建一个扩展,以便执行与文件系统为 google chrome 所做的相同的事情?


Ubuntu 32 位

加载 1gb+ ajax 仅用于监视下载进度和填充内存并不方便。

相反,我只发送带有 Content-Disposition header 的文件以保存文件。


然而,有一些方法可以绕过它来监控进度。选项一是使用第二个 websocket 来指示您在使用 get 请求正常下载时下载了多少。另一个选项稍后在底部描述


我知道你在谈话中谈到了使用 Blinks 沙盒文件系统。但它有一些缺点。如果使用持久存储,它可能需要许可。它只允许剩余 20% 的可用磁盘。如果 chrome 需要释放一些 space 那么它会丢弃最后用于最近文件的任何其他域临时存储。此外,它在私人模式下不起作用。
更不用说它一直在放弃对它的支持,并且可能永远不会出现在其他浏览器中——但它们很可能不会删除它,因为许多网站仍然依赖它


处理这个大文件的唯一方法是使用流。这就是我创建 StreamSaver 的原因。这仅适用于 Blink(chrome 和 opera)ATM,但它最终会被其他具有 whatwg 规范的浏览器支持,以将其作为标准进行备份。

fetch(url).then(res => {
    // One idea is to get the filename from Content-Disposition header...
    const size = ~~res.headers.get('Content-Length')
    const fileStream = streamSaver.createWriteStream('filename.zip', size)
    const writeStream = fileStream.getWriter()
    // Later you will be able to just simply do
    // res.body.pipeTo(fileStream)
    // instead of pumping

    const reader = res.body.getReader()
    const pump = () => reader.read()
        .then(({ value, done }) => {
            // here you know how large the value (chunk) is and you can
            // figure out the download speed/progress when comparing it to the size

            return done 
                ? writeStream.close()
                : writeStream.write(value).then(pump)
        )

    // Start the reader
    pump().then(() =>
        console.log('Closed the stream, Done writing')
    )
})

这不会占用任何内存

我有一个理论,如果你将文件分成块并将它们存储在 indexedDB 中,然后将它们合并在一起,它就会起作用

Blob 不是由数据组成的...它更像是指向可以从中读取文件的位置的指针

意味着如果您将它们存储在 indexedDB 中然后执行类似的操作(使用 FileSaver 或替代方法)

finalBlob = new Blob([blob_A_fromDB, blob_B_fromDB])
saveAs(finalBlob, 'filename.zip')

但是我还没有测试过,无法确认,如果有人可以的话就更好了