如何使用Javascript下载、压缩和保存多个文件并取得进展?
我正在创建一个需要从网站下载多个文件(图像和/或视频)的Chrome扩展。这些文件可能很大,所以我想向用户显示下载进度。经过一些研究,我发现目前一个可能的解决方案可能是:如何使用Javascript下载、压缩和保存多个文件并取得进展?,javascript,google-chrome-extension,zip,download,save-as,Javascript,Google Chrome Extension,Zip,Download,Save As,我正在创建一个需要从网站下载多个文件(图像和/或视频)的Chrome扩展。这些文件可能很大,所以我想向用户显示下载进度。经过一些研究,我发现目前一个可能的解决方案可能是: 使用XMLHttpRequests下载所有文件 下载后,使用JavaScript库(例如JSZip.js、zip.js)将所有文件压缩到一个归档文件中 提示用户使用“另存为”对话框保存zip 我被困在第二段),我如何才能压缩下载的文件 为了理解,下面是一个代码示例: var fileURLs = ['http://www.te
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)
...
}
}
var fileurl=['http://www.test.com/img.jpg',...];
var zip=newjszip();
var计数=0;
对于(var i=0;i zip.file(fileName,blob_url);//我想起了这个问题。由于它还没有答案,我写了一个可能的解决方案,以防它对其他人有用:
- 如前所述,第一个问题是将blob url传递给jszip(它不支持blob,但也不会抛出任何错误来通知,并且它成功地生成了损坏文件的存档):要纠正这个问题,只需传递一个base64数据字符串,而不是它的blob对象url
- 第二个问题是文件名同步:这里最简单的解决方法是一次下载一个文件,而不是使用parallels xhr请求
因此,修改后的上限代码可以是:
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) {
...
}
}
var fileurl=['http://www.test.com/img.jpg',...];
var zip=newjszip();
var计数=0;
下载文件(fileURLs[count],onDownloadComplete);
函数下载文件(url,onSuccess){
var xhr=new XMLHttpRequest();
xhr.onprogress=calculateAndUpdateProgress;
xhr.open('GET',url,true);
xhr.responseType=“blob”;
xhr.onreadystatechange=函数(){
if(xhr.readyState==4){
if(onSuccess)onSuccess(xhr.response);
}
函数onDownloadComplete(blobData){
if(count
最后请注意,如果您下载的文件很少(不到10个文件的总大小小于1MB),此解决方案工作得很好,在其他情况下,JSZip将在生成归档文件时使浏览器选项卡崩溃,因此最好使用单独的线程进行压缩(像zip.js这样的WebWorker)
如果在生成存档文件之后,浏览器仍然会因大文件而崩溃,并且没有报告任何错误,请尝试在不传递二进制数据的情况下触发“另存为”窗口,但要传递blob引用(a.href=URL.createObjectURL(zippedBlobData);
其中zippedBlobData
是引用生成的存档数据的blob对象);基于@guari代码,我在本地对其进行了测试,并将其应用于react应用程序,附加代码供其他人参考
从“JSZip”导入JSZip;
从“jszip/vendor/FileSaver.js”导入saveAs;
// .......
//下载按钮点击事件
btnDownloadAudio=记录=>{
让FileURL=['https://www.test.com/52f6c50.AMR', 'https://www.test.com/061940.AMR'];
让计数=0;
让zip=newjszip();
const query={record,fileurl,count,zip};
this.downloadFile(查询,this.onDownloadComplete);
}
下载文件=(查询,onSuccess)=>{
const{fileurl,count,}=query;
var xhr=new XMLHttpRequest();
xhr.onprogress=this.calculateAndUpdateProgress;
open('GET',fileurl[count],true);
xhr.responseType=“blob”;
xhr.onreadystatechange=函数(e){
if(xhr.readyState==4){
if(onSuccess)onSuccess(查询,xhr.response);
}
}
xhr.send();
}
onDownloadComplete=(查询,blobData)=>{
让{record,fileurl,count,zip}=query;
if(countimport JSZip from 'jszip'
import JSZipUtils from 'jszip-utils'
import FileSaver from 'file-saver'
const async downloadZip = (urls) => {
const urlToPromise = (url) => {
return new Promise((resolve, reject) => {
JSZipUtils.getBinaryContent(url, (err, data) => {
if (err) reject(err)
else resolve(data)
})
})
}
const getExtension = (binary) => {
const arr = (new Uint8Array(binary)).subarray(0, 4)
let hex = ''
for (var i = 0; i < arr.length; i++) {
hex += arr[i].toString(16)
}
switch (hex) {
case '89504e47':
return 'png'
case '47494638':
return 'gif'
case 'ffd8ffe0':
case 'ffd8ffe1':
case 'ffd8ffe2':
case 'ffd8ffe3':
case 'ffd8ffe8':
return 'jpg'
default:
return ''
}
}
this.progress = true
const zip = new JSZip()
for (const index in urls) {
const url = urls[index]
const binary = await urlToPromise(url)
const extension = getExtension(binary) || url.split('.').pop().split(/#|\?/)[0]
const filename = `${index}.${extension}`
zip.file(filename, binary, { binary: true })
}
await zip.generateAsync({ type: 'blob' })
.then((blob) => {
FileSaver.saveAs(blob, 'download.zip')
})
}
downloadZip(['https://example.net/1.jpg', 'https://example.net/some_picture_generator'])