我正在创建一个 Chrome 扩展程序,需要从网站下载多个文件(图像和/或视频)。这些文件可能很大,所以我想向用户显示下载进度。经过一番研究,我发现目前可能的解决方案可能是:
- 使用 XMLHttpRequests 下载所有文件。
- 下载后,使用 JavaScript 库(例如 JSZip.js、zip.js)将所有文件压缩到一个存档中。
- 使用“另存为”对话框提示用户保存 zip。
我被困在第 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 来完成这样一个简单的任务。
我想起了这个问题..由于它还没有答案,我写了一个可能的解决方案,以防对其他人有用:
- 如上所述,第一个问题是将 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);
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);
where zippedBlobData
是引用生成的存档数据的 blob 对象);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)