如何使用 Javascript 下载、压缩和保存多个文件并获得进度?

2023-11-24

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

  1. 使用 XMLHttpRequests 下载所有文件。
  2. 下载后,使用 JavaScript 库(例如 JSZip.js、zip.js)将所有文件压缩到一个存档中。
  3. 使用“另存为”对话框提示用户保存 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(使用前将#替换为@)

如何使用 Javascript 下载、压缩和保存多个文件并获得进度? 的相关文章

随机推荐

  • intelliJ idea 10 社区版和 GWT 插件

    安装 GWT 插件遇到困难 尝试搜索 GWT 插件 我所能找到的只是 GWT Imagebundle 它已经非常过时了 尝试了 文件菜单 gt 配置插件 但没有帮助 因为插件尚未安装 intelliJ 10 社区版是否支持 GWT 插件 如
  • Angular2 - Http POST 请求参数

    我正在尝试发出 POST 请求 但无法使其正常工作 testRequest var body username myusername password mypassword var headers new Headers headers a
  • css3 webkit动画停止在div上:悬停

    我尝试使用 webkit animation 和 webkit keyframes 制作动画 我有一个 div 动画 里面有子 div 当我的鼠标位于孩子上方时 我会停止父母的 webkit 动画 有例子吗 Thanks 不幸的是CSS中没
  • Linux 上的 openSSL 证书验证 [关闭]

    Closed 这个问题是无关 目前不接受答案 JKJS 我有这个证书链 cert pem self signed gt cert pem gt cert pem 所有三个证书都是我生成的 任何地方都没有使用互联网连接 这是完美的离线工作 现
  • XSS 安全。来自同一域的 2 个 iframe 之间的通信

    域 abc com 有一个包含 2 个 iframe 的页面 它们都是从域 xyz com 加载的 XSS 安全性是否会阻止这两个 iframe 之间的 JavaScript 访问 通信 交互 嗯 这取决于你所说的沟通是什么意思 似乎某种类
  • 访客设计模式-返回类型

    我使用访客设计模式来解决我们系统中的问题之一 作为如何实现它的参考 我使用了工厂现场 and 这个 YouTube 视频 在 DoFactory 示例中 访问者使用返回类型为 void 的方法 而在 YouTube 视频中 作者使用 dou
  • 将代码从 Mathematica 复制到其他平台的已知问题?

    我刚刚注意到 如果你在 MMA 8 0 1 win7 64 中有这个 然后你将它复制到SO 只需ctrl c ctrl v 你会得到这个 maxY minY stepy maxX minX stepx 这是数学上不等价 应该是这样的 max
  • AVCaptureSession 收到内存警告并无故崩溃

    我正在开发一个处理高清照片的应用程序 我正在使用 AVCaptureSession 拍照 停止它 然后对该照片应用效果 让我疯狂的是 一切都运行良好 仪器告诉我 我正确且按时地释放了我使用的所有内存 是的 它确实很高 有时达到 100mb
  • 为什么 Xcode 构建配置的精确副本会失败?

    我有一个react native具有桥接 Swift 代码的应用程序 可以使用 Xcode 毫无问题地构建 运行和存档10 0使用默认的Release and Debug构建配置 我用Xcode gt Product gt Archive或
  • 如何叠加不同单元格的图?

    在我笔记本的一个单元格中 我已经用以下内容绘制了一些内容 myplot plt figure plt plot x y 现在 在不同的单元格中 我想再次绘制完全相同的图形 但在其顶部添加新的图形 类似于两次连续调用所发生的情况 plt pl
  • 角度材质 - 更改单击的垫列表选项的颜色

    是否可以更改选中复选框的默认颜色 mat pseudo checkbox checked
  • preg_match_all() [function.preg-match-all]: 未知修饰符 ']'

    使用了几种不同的模式 但它们都出现了这个错误 那么出了什么问题呢 我要诊断的最短的一个是 pattern
  • 为什么使用“*”构建视图不好?

    为什么使用 构建视图不好 假设您有一个复杂的联接 并且所有字段都可能在某处使用 然后您只需选择所需的字段即可 SELECT field1 field2 FROM aview WHERE 视图 aview 可以是SELECT table1 t
  • 不需要导出仅包含虚拟/内联函数的类?

    在 Win32 上的 C 中 假设我有一个带有声明类的头文件的 DLL DLL 导出一些获取该类实例的指针 引用的方法 例如工厂函数 我是否正确地认为 如果只在其实例上调用虚拟或内联函数 则无需使用 declspec 将该类标记为导出 相反
  • 导入错误:无法导入名称 _imaging

    我安装了Pillow 然后我想做 from PIL import Image 我收到以下错误 Traceback most recent call last File
  • 如何在 MySQL 中搜索嵌套 JSON

    我使用 MySQL 5 7 和本机 JSON 数据类型 样本数据 code 2 stores code 100 quantity 2 code 200 quantity 3 code 4 stores code 300 quantity 4
  • 将同一对象两次添加到 ManyToManyField

    我有两个 Django 模型类 class A models Model name models CharField max length 128 irrelevant class B models Model a models ManyT
  • Mac OS X shell 实用程序,显示多核系统中各个 CPU 的使用情况

    我一直在寻找一个 osx 实用程序来显示每个 cpu 的 cpu 使用情况 例如 中央处理器 0 10 中央处理器 1 2 我知道有很多方法可以在其他类 Unix 系统 proc mpstat 等 中获取此信息 但在 osx 中都不起作用
  • 检查对象数组是否包含某个键

    我需要确定对象数组中是否存在某个键 这是一个示例数组 arrOfObj mainKey1 subKey1 innerKey1 innerMostKey1 key1 value mainKey2 key2 value mainKe
  • 如何使用 Javascript 下载、压缩和保存多个文件并获得进度?

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