首页 > 编程语言 >JavaScript打包下载最佳实践(StreamSaver.js+zip-stream.js流式下载)

JavaScript打包下载最佳实践(StreamSaver.js+zip-stream.js流式下载)

时间:2022-10-31 12:33:58浏览次数:90  
标签:文件 StreamSaver name stream js 下载 打包

StreamSaver.js + zip-stream.js流式下载&压缩文件。
部分浏览器(火狐)可能不兼容。

1 应用场景

在实际项目中,通常存在用户手动选择下载多个文件的情况。
常规的做法(服务器打包下载)是,后端从文件服务器(比如华为云OBS)读取文件,将这些文件进行打包,然后将压缩包字节流返回给前端,由前端下载到用户本地文件系统。
服务器打包下载存在以下问题:

  1. 文件传输链路:文件服务器→应用服务器,应用服务器→浏览器,消耗传输时间和服务器的流量。
  2. 服务器打包,消耗性能。
  3. 大文件下载和打包,通常会连接超时。

另一种做法(浏览器打包下载)是,后端生成多个文件服务临时访问地址,前端直接从文件服务器下载文件,将这些文件进行打包,然后下载到用户本地文件系统。
浏览器打包下载有以下优点:

  1. 文件传输链路:文件服务器 → 浏览器,明显缩短传输时间,减少服务器的流量和性能消耗。
  2. 将大请求分解成一个个小请求,避免了请求超时的问题。
  3. 文件服务器通常不会设置请求超时时间。

但是,浏览器打包下载也存在以下缺点:

  1. 前端通过AJAX请求文件服务器,需要文件服务器支持跨域。
  2. 前端下载文件消耗浏览器内存,可能会造成OOM(Out of Memory)。

对于第一个缺点,文件服务器通常可以在管理页面进行跨域配置,因此一般都可以解决。
对于第二个缺点,以往思路是先将文件下载到浏览器内存,然后再对每个文件进行压缩操作。这种方法对下载文件大小有限制。
StreamSaver.js可以对数据进行流式处理,结合zip-stream.js可以做到实时将文件流进行压缩,直接保存到用户本地文件系统。因此,除了下载速度>压缩速度那一部分缓存,可以大大减小浏览器内存的占用。

2 使用

StreamSaver.js + zip-stream.js流式下载&压缩文件的使用方式很简单。

2.1 引入js文件

由于某些浏览器不支持流式处理,可以按需要引入:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ponyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/eligrey/Blob.js/Blob.js"></script>

然后引入StreamSaver.jszip-stream.js(可以根据以下链接直接下载到本地):

<script src="https://jimmywarting.github.io/StreamSaver.js/examples/zip-stream.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/StreamSaver.min.js"></script>

2.2 定义打包下载函数

定义同步打包下载函数zipFiles()

/**  
 * 同步下载打包【推荐】 
 * @param zipName 压缩包文件名  
 * @param files 文件列表,格式:[{"name":"文件名", "url":"文件下载地址"},……]  
 */
 function zipFiles(zipName, files) {  
    console.log("同步下载打包开始时间:" + new Date());  
    // 创建压缩文件输出流  
    const zipFileOutputStream = streamSaver.createWriteStream(zipName);  
    // 创建下载文件流  
    const fileIterator = files.values();  
    const readableZipStream = new ZIP({  
        async pull(ctrl) {  
            const fileInfo = fileIterator.next();  
            if (fileInfo.done) {//迭代终止  
                ctrl.close();  
            } else {  
                const {name, url} = fileInfo.value;  
                return fetch(url).then(res => {  
                    ctrl.enqueue({  
                        name,  
                        stream: () => res.body  
                    });  
                })  
            }        }    });  
    if (window.WritableStream && readableZipStream.pipeTo) {  
        // 开始下载  
        readableZipStream  
            .pipeTo(zipFileOutputStream)  
            .then(() => console.log("同步下载打包结束时间:" + new Date()));  
    }  
}

需要注意的是,浏览器只会使用一个Service Worker线程进行压缩,整体打包下载速度取决于压缩速度。因此对于多个文件,异步下载的加速效果没有那么明显,反而可能会使浏览器内存占用过多,造成浏览器内存溢出。
以下是异步下载打包的方法,但不推荐使用:

/**  
 * 异步下载打包【不推荐,超大文件时可能会造成浏览器内存溢出】   
 * @param zipName  压缩包文件名
 * @param files 文件列表,格式:[{"name":"name", "url":"url"},……] 
 */  
function asyncZipFiles(zipName, files) {  
    console.log("异步下载打包开始时间:" + new Date());  
    // 创建压缩文件  
    const zipFileOutputStream = streamSaver.createWriteStream(zipName);  
    // 创建下载文件流  
    const readableZipStream = new ZIP({  
        async pull(ctrl) {  
            // promise任务  
            const promise = el => {  
                let name = el.name  
                return new Promise(resolve => {  
                    fetch(el.url).then(resp => {  
                        if (resp.status === 200) {  
                            return () => resp.body;  
                        }  
                        return null;  
                    }).then(stream => {  
                        resolve({name: name, stream: stream});  
                    })  
                })            }            // promise任务队列  
            let arr = [];  
            files.forEach(el => {  
                arr.push(promise(el));  
            })  
            // 异步下载  
            await Promise.all(arr).then(res => {  
                let nameMapList = []  
                res.forEach(item => {  
                    const name = item.name;  
                    const stream = item.stream;  
                    // 加入打包队列  
                    if (stream) {  
                        let file = {name, stream};  
                        ctrl.enqueue(file);  
                    }  
                })            })            ctrl.close();  
        }  
    });  
    if (window.WritableStream && readableZipStream.pipeTo) {  
        // 开始下载  
        readableZipStream  
            .pipeTo(zipFileOutputStream)  
            .then(() => console.log("异步下载打包结束时间:" + new Date()))  
    }}

2.3 调用函数进行下载

当用户选择好要下载的文件,点击下载按钮时,可以构造打包下载的参数进行下载:

let zipName = '压缩包.zip';  
let files = [  
    {   "name": '2022102801.mp4',  
        "url": 'http://localhost:8080/file3'  
    }, 
    {  
        "name": '文件夹1/2022102802.mp4',  
        "url": 'http://localhost:8080/file4'  
    },  
    {  
        "name": '文件夹1/2022102803.mp4',  
        "url": 'http://localhost:8080/file5'  
    },  
    {  
        "name": '文件夹3/文件夹3/文件夹3/2022102804.mp4',  
        "url": 'http://localhost:8080/file1?fileUrl=http://mirror.aarnet.edu.au/pub/TED-talks/911Mothers_2010W-480p.mp4'  
    }  
];  
zipFiles(zipName, files);

压缩包内文件夹以/分隔,可以自定义需要的压缩包层级关系。

标签:文件,StreamSaver,name,stream,js,下载,打包
From: https://www.cnblogs.com/Xianhuii/p/16843884.html

相关文章

  • 终于学会了,SpringBoot整合JSP,建议收藏不然找不到了
    SpringBoot整合JSP,一个经典而且优雅的方案!步骤1pom.xml<!--servlet依赖.--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api<......
  • 百度在线盘,没有VIP,Antdownload,又一款度盘不限速下载器!
    目前,百度网盘在非会员状态下,下载相关资源,速度仍然是几十KB/S,这龟速实在难受。百度网盘速度限制,我提供了多种方法来“解除速度限制”。满速下载百度网盘资源的方法总共有两类......
  • IDEA版SpringBoot全教程 04 整合JSP
    <!--servlet依赖.--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId></dependency><dependency>......
  • 最简vue.js原理教程,适合初学者
    1.我们要做什么?早就想写这个了,和csdn高校俱乐部约好了有个直播,想着反正要备课,我不如直接把要讲的东西写成博客算了。说到vue,我们自然就想到数据绑定。说到数据绑定,自然就想......
  • windows 开机自动运行nodejs项目 pm2方法实现
    PM2是带有内置负载平衡器的Node.js应用程序的生产过程管理器。可以利用它来简化很多Node应用管理的繁琐任务,如性能监控、自动重启、负载均衡等。安装部署1、我们一......
  • js常用方法和一些封装(2) -- 随机数生成
    任何编程语言,随机数都是必不可少的,我在开发过程中,尤其在自己做一些小玩意的时候,就经常使用随机数,后来发现每次使用都懒得写,直接去网上搜一个,拿过来就用了。可是时间一长,发现......
  • js进阶手写常见函数
    JavaScript进阶的必要性无论是学习react还是vue,它们都是js的应用框架。剥去他们的壳子看到的始终是js,所以作为一个前端大厨必须要熟练掌握好js这个大勺,才能烧出一顿好菜......
  • js异步编程的三种模式
    写在前面javascript语言的执行环境是"单线程"(singlethread),就是指一次只能完成一件任务。如果有多个任务,就必须排队,等前面一个任务完成,再执行后面一个任务,以此类推。......
  • js对象和原型、原型链的关系
    JS的原型、原型链一直是比较难理解的内容,不少初学者甚至有一定经验的老鸟都不一定能完全说清楚,更多的"很可能"是一知半解,而这部分内容又是JS的核心内容,想要技术进阶的话肯......
  • 一文读懂NodeJs知识体系和原理浅析
    node.js初探Node.js是一个JS的服务端运行环境,简单的来说,它是在JS语言规范的基础上,封装了一些服务端的运行时对象,让我们能够简单实现非常多的业务功能。如果我们只......