我正在创建一个Chrome扩展程序,需要从网站下载多个文件(图像和/或视频)。这些文件可能有很大的大小,所以我想向用户显示下载进度。经过一些研究,我发现目前可能的解决方案可能是:
我被困在第2段),我如何压缩下载的文件?
要理解,这是一个代码示例:
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)
...
}
}
上面的代码生成一个包含小损坏文件的可下载档案。
文件名同步也存在问题:blob对象不包含文件名,因此如果。 fileURLs[0]
下载的时间比fileURLs[1]
名称错误(倒置)需要更多时间..
注意:我知道Chrome有一个下载API,但是它位于开发频道,所以不幸的是它现在不是解决方案,我想避免使用NPAPI来完成这么简单的任务。
答案 0 :(得分:7)
我被提醒了这个问题..由于它还没有答案,我写了一个可能的解决方案,以防它对其他人有用:
因此,修改后的上层代码可以是:
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) {
...
}
}
最后请注意,如果您下载少量文件(小于10个文件的整体大小不到1MB),此解决方案效果很好,在其他情况下,JSZip会在生成存档时崩溃浏览器选项卡,因此使用分离的线程进行压缩是更好的选择(WebWorker,如zip.js)。
如果在此之后生成了存档,浏览器仍会保持与大文件崩溃并且不报告任何错误,尝试触发saveAs窗口而不传递二进制数据,但是通过传递blob引用(a.href = URL.createObjectURL(zippedBlobData);
其中zippedBlobData
是blob对象,它引用生成的归档数据);
答案 1 :(得分:0)
基于@guari代码,我在本地对其进行了测试,并将其应用于react应用程序,并附加了该代码以供他人参考。
import JSZip from "jszip";
import saveAs from "jszip/vendor/FileSaver.js";
// .......
// download button click event
btnDownloadAudio = record =>{
let fileURLs = ['https://www.test.com/52f6c50.AMR', 'https://www.test.com/061940.AMR'];
let count = 0;
let zip = new JSZip();
const query = { record, fileURLs, count, zip };
this.downloadFile(query, this.onDownloadComplete);
}
downloadFile = (query, onSuccess) => {
const { fileURLs, count, } = query;
var xhr = new XMLHttpRequest();
xhr.onprogress = this.calculateAndUpdateProgress;
xhr.open('GET', fileURLs[count], true);
xhr.responseType = "blob";
xhr.onreadystatechange = function (e) {
if (xhr.readyState == 4) {
if (onSuccess) onSuccess(query, xhr.response);
}
}
xhr.send();
}
onDownloadComplete = (query, blobData) => {
let { record, fileURLs, count, zip } = query;
if (count < fileURLs.length) {
const _this = this;
const { audio_list, customer_user_id, } = record;
this.blobToBase64(blobData, function(binaryData){
// add downloaded file to zip:
var sourceFileName = fileURLs[count].substring(fileURLs[count].lastIndexOf('/')+1);
// convert the source file name to the file name to display
var displayFileName = audio_list[count].seq + sourceFileName.substring(sourceFileName.lastIndexOf('.'));
zip.file(displayFileName, binaryData, {base64: true});
if (count < fileURLs.length -1){
count++;
_this.downloadFile({ ...query, count }, _this.onDownloadComplete);
}
else {
// all files have been downloaded, create the zip
zip.generateAsync({type:"blob"}).then(function(content) {
// see FileSaver.js
saveAs(content, `${customer_user_id}.zip`);
});
}
});
}
}
blobToBase64 = (blob, callback) => {
var reader = new FileReader();
reader.onload = function() {
var dataUrl = reader.result;
var base64 = dataUrl.split(',')[1];
callback(base64);
};
reader.readAsDataURL(blob);
}
calculateAndUpdateProgress = (evt) => {
if (evt.lengthComputable) {
// console.log(evt);
}
}
答案 2 :(得分:0)
import 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'])