如何使用Javascript下载,压缩和保存多个文件并获得进展? [英] How to download, zip and save multiple files with Javascript and get progress?

查看:622
本文介绍了如何使用Javascript下载,压缩和保存多个文件并获得进展?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一个Chrome扩展程序,需要从网站下载多个文件(图像和/或视频)。这些文件可能有一个巨大的大小,所以我想向用户显示下载进度。经过一些研究,我发现目前可能的解决方案可能是:


  1. 使用XMLHttpRequests下载所有文件。

  2. 下载时,使用JavaScript库(例如JSZip.js,zip.js)将所有文件压缩到一个存档中。

  3. 提示用户使用SaveAs对话框保存zip

我被困在第2段),如何压缩下载的文件?



要了解,这里是一个代码示例:

  var fileURLs = ['http:// www。 test.com/img.jpg,...]; 
var zip = new JSZip();

var count = 0; (var i = 0; i< fileURLs.length; i ++){
var xhr = new XMLHttpRequest();

xhr.onprogress = calculateAndUpdateProgress;
xhr.open('GET',fileURLs [i],true);
xhr.responseType =blob;
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
var blob_url = URL.createObjectURL(response);
//将下载的文件添加到zip:
var fileName = fileURLs [count] .substring(fileURLs [count] .lastIndexOf('/')+ 1);
zip.file(fileName,blob_url); //< - 这里有一个问题

count ++;
if(count == fileURLs.length){
//全部下载完成,创建zip
var content = zip.generate();

//然后触发下载链接:
var zipName ='download.zip';
var a = document.createElement('a');
a.href =data:application / zip; base64,+ content;
a.download = zipName;
a.click();
}
}
};
xhr.send();
}

函数calculateAndUpdateProgress(evt){
if(evt.lengthComputable){
//通过执行一些平均
//计算得到下载进度使用evt.loaded,evt.total和
//的文件下载/已经下载
...
//然后更新GUI元素(例如,页面动作图标和弹出如果显示)
...
}
}

上面的代码生成一个包含小损坏文件的可下载的存档。
文件名sync也有一个问题:blob对象不包含文件名,所以如果eg。 fileURLs [0] 需要比 fileURLs [1] 更多的时间被下载到错误(反转)..



注意:我知道Chrome有一个下载API,但它在开发频道,所以不幸的是现在不是一个解决方案,我想避免使用NPAPI这样一个简单的任务

解决方案

我被提醒了这个问题..由于还没有答案,我写了一个可能的解决方案,以防它可以对其他人有用:




  • 如上所述,第一个问题是将blob url传递给jszip(它不支持blob,但也是不会发生任何错误通知,并且它成功生成损坏文件的归档):要纠正这一点,只需传递一个base64字符串的数据,而不是其blob对象url;

  • 第二个问题是文件名同步:这里最简单的解决方法是一次下载一个文件,而不是使用并行xhr请求。



所以,修改的上层代码可以是:

  var fileURLs = ['http://www.test.com/img.jpg',...]; 
var zip = new JSZip();
var count = 0;

downloadFile(fileURLs [count],onDownloadComplete);


函数downloadFile(url,onSuccess){
var xhr = new XMLHttpRequest();
xhr.onprogress = calculateAndUpdateProgress;
xhr.open('GET',url,true);
xhr.responseType =blob;
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(onSuccess)onSuccess(xhr.response);
}

函数onDownloadComplete(blobData){
if(count< fileURLs.length){
blobToBase64(blobData,function(binaryData){
//将下载的文件添加到zip:
var fileName = fileURLs [count] .substring(fileURLs [count] .lastIndexOf('/')+ 1);
zip.file(fileName,binaryData, base64:true});
if(count< fileURLs.length -1){
count ++;
downloadFile(fileURLs [count],onDownloadCompleted);
}
else {
//所有文件已下载,创建zip
var content = zip.generate();

//然后触发下载链接:
var zipName ='download.zip';
var a = document.createElement('a');
a.href =data:application / zip; base64,+ co ntent;
a.download = zipName;
a.click();
}
});
}
}

函数blobToBase64(blob,callback){
var reader = new FileReader();
reader.onload = function(){
var dataUrl = reader.result;
var base64 = dataUrl.split(',')[1];
回调(base64);
};
reader.readAsDataURL(blob);
}

函数calculateAndUpdateProgress(evt){
if(evt.lengthComputable){
...
}
}

最后一个注意事项,如果您下载少量文件(大约少于1MB,整体尺寸小于1MB)少于10个文件),在其他情况下,当要生成存档时,JSZip将崩溃浏览器选项卡,因此使用分离的线程进行压缩(一个WebWorker,如zip.js)将是一个更好的选择。 / p>

如果之后存档已经生成,浏览器仍然保持大文件崩溃,没有报告任何错误,尝试触发saveAs窗口而不传递二进制数据,但由传递一个blob引用( a.href = URL.createObjectURL(zippedBlobData); 其中 zippedBlobData 是引用的blob对象到生成的归档数据);


I'm creating a Chrome extension that needs to download multiple files (images and/or videos) from a website. These files may have a huge size, so I want to show the download progress to the user. After some research I found that currently a possible solution might be:

  1. Download all the files with XMLHttpRequests.
  2. When downloaded, zip all the files into one archive with a JavaScript library (eg. JSZip.js, zip.js).
  3. Prompt the user to save the zip with SaveAs dialog.

I'm stuck at passage 2), how can I zip the downloaded files?

To understand, here is a code sample:

var fileURLs = ['http://www.test.com/img.jpg',...];
var zip = new JSZip();

var count = 0;
for (var i = 0; i < fileURLs.length; i++){
    var xhr = new XMLHttpRequest();
    xhr.onprogress = calculateAndUpdateProgress;
    xhr.open('GET', fileURLs[i], true);
    xhr.responseType = "blob";
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
               var blob_url = URL.createObjectURL(response);
            // add downloaded file to zip:
            var fileName = fileURLs[count].substring(fileURLs[count].lastIndexOf('/')+1);
            zip.file(fileName, blob_url); // <- here's one problem

            count++;
            if (count == fileURLs.length){
                // all download are completed, create the zip
                var content = zip.generate();

                // then trigger the download link:
                var zipName = 'download.zip';
                var a = document.createElement('a'); 
                a.href = "data:application/zip;base64," + content;
                a.download = zipName;
                a.click();
            }
        }
    };
    xhr.send();
}

function calculateAndUpdateProgress(evt) {
    if (evt.lengthComputable) {
        // get download progress by performing some average 
        // calculations with evt.loaded, evt.total and the number
        // of file to download / already downloaded
        ...
        // then update the GUI elements (eg. page-action icon and popup if showed)
        ...
    }
}

The upper code generate a downloadable archive containing small corrupted files. There is also an issue with filename sync: blob object do not contains the file name, so If eg. fileURLs[0] takes more time to be downloaded than fileURLs[1] names become wrong (inverted)..

NOTE: I know that Chrome has a download API but it's in dev channel so unfortunately it's not a solution now, and I would like to avoid using NPAPI for such a simple task.

解决方案

I was reminded of this question.. since it has no answers yet, I write a possible solution in case it can be useful to someone else:

  • as said, the first problem is with passing blob url to jszip (it does not support blobs but it also does not throw any error to notify that and it successfully generates an archive of corrupted files): to correct this, simply pass a base64 string of the data instead of its blob object url;
  • the second problem is with file name synchronization: the easiest workaround here is to download one file at a time instead of using parallels xhr requests.

So, the modified upper code can be:

var fileURLs = ['http://www.test.com/img.jpg',...];
var zip = new JSZip();
var count = 0;

downloadFile(fileURLs[count], onDownloadComplete);


function downloadFile(url, onSuccess) {
    var xhr = new XMLHttpRequest();
    xhr.onprogress = calculateAndUpdateProgress;
    xhr.open('GET', url, true);
    xhr.responseType = "blob";
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
            if (onSuccess) onSuccess(xhr.response);
}

function onDownloadComplete(blobData){
    if (count < fileURLs.length) {
        blobToBase64(blobData, function(binaryData){
                // add downloaded file to zip:
                var fileName = fileURLs[count].substring(fileURLs[count].lastIndexOf('/')+1);
                zip.file(fileName, binaryData, {base64: true});
                if (count < fileURLs.length -1){
                    count++;
                    downloadFile(fileURLs[count], onDownloadCompleted);
                }
                else {
                    // all files have been downloaded, create the zip
                    var content = zip.generate();

                    // then trigger the download link:        
                    var zipName = 'download.zip';
                    var a = document.createElement('a'); 
                    a.href = "data:application/zip;base64," + content;
                    a.download = zipName;
                    a.click();
                }
            });
    }
}

function blobToBase64(blob, callback) {
    var reader = new FileReader();
    reader.onload = function() {
        var dataUrl = reader.result;
        var base64 = dataUrl.split(',')[1];
        callback(base64);
    };
    reader.readAsDataURL(blob);
}

function calculateAndUpdateProgress(evt) {
    if (evt.lengthComputable) {
        ...
    }
}

Last note, this solution works quite well if you download few and little files (about less than 1MB as whole size for less than 10 files), in other cases JSZip will crash the browser tab when the archive is going to be generated, so it will be a better choice to use a separated thread for compression (a WebWorker, like zip.js does).

If after that the archive has been generated, the browser still keeps crashing with big files and without reporting any errors, try to trigger the saveAs window without passing binary data, but by passing a blob reference (a.href = URL.createObjectURL(zippedBlobData); where zippedBlobData is the blob object that refers to the generated archive data);

这篇关于如何使用Javascript下载,压缩和保存多个文件并获得进展?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆