Javascript中的大blob文件

时间:2016-08-31 15:10:23

标签: javascript firefox large-files

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

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();
}

但File 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);
    });
});

Web Worker

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并在浏览器下载后将其发送给用户

我将使用FileSystem API for Google Chrome,但我想为firefox制作一些内容,我查看了File Handle Api,但没有...

我是否必须为firefox构建扩展,以便像FileSystem那样为google chrome做同样的事情?

Ubuntu 32位

2 个答案:

答案 0 :(得分:6)

使用ajax加载1gb +不方便仅用于监控下载进度和填满内存。

相反,我只是发送带有Content-Disposition标题的文件来保存文件。

但是有一些方法可以绕过它来监控进度。选项一是有一个第二个websocket,它指示你在正常下载获取请求时已经下载了多少。另一个选项将在后面的

中描述

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

处理这个大文件的唯一方法是使用流。这就是我创建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')
    )
})

这不会占用任何内存

答案 1 :(得分:4)

我有一个理论,即如果您将文件拆分为块并将它们存储在indexedDB中,然后将它们合并在一起它将起作用

blob不是由数据构成的...它更像是指向文件可以从哪里读取的指针 enter image description here

这意味着你将它们存储在indexedDB中然后做这样的事情(使用FileSaver或替代)

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

但是我无法证实这一点,因为我还没有对它进行过测试,如果有其他人能够做到这一点会很好