StreamSaver.js入门教程:优雅解决前端下载文件的难题
本文简介
点赞 + 关注 + 收藏 = 学会了
本文介绍一个能让前端优雅下载大文件的工具:StreamSaver.js
- ⚡️ StreamSaver.js GitHub地址
- ⚡️ 官方案例
StreamSaver.js
可用于实现在Web浏览器中直接将大文件流式传输到用户设备的功能。
传统的下载方式可能导致大文件的加载时间较长或造成内存占用过大的问题,使用 <a>
标签打开新页面下载文件,遇到 .txt
或者 .mp4
之类的文件可能就直接在页面展示了,不会触发下载功能。而 StreamSaver.js
则通过流式下载的方式解决了这些问题。
StreamSaver.js
将大文件拆分成小块,并在下载过程中逐块传输到硬盘,从而降低内存占用和提高下载速度。
环境准备
要学习 StreamSaver.js
首先要准备一份或者多份可下载的文件。
你可以使用网络上的文件资源,但这需要你自己去找。
你也可以在自己的电脑运行个服务,把文件资源丢进去即可。
如果你用脚手架创建项目,比如vue或者react之类的项目,也可以把文件放在静态资源目录里。
比如用 vite
创建一个 Vue
项目,然后在 public
目录下创建一个 test.txt
文件。项目运行起来,在浏览器访问 http://localhost:端口号/public/test.txt
就能查看到这个文件内容。
安装 StreamSaver.js
可以使用 CDN
或者 npm
安装 StreamSaver.js
。
本文使用 CDN
的方式讲解。
CDN
打开 StreamSaver.js的仓库。
把 StreamSaver.js
文件下载到你项目里引入即可。
<script src="../StreamSaver.js"></script>
npm
⚡️StreamSaver npm地址
使用以下命令下载 StreamSaver
到项目里
npm i streamsaver
然后在要使用的地方引入即可。
import streamSaver from "streamsaver"
起步
起步阶段,我们先试试如何下载一个 .txt
文件。
如果我们要下载一些浏览器读不懂的文件,我们可以使用 <a>
标签在新窗口打开链接,也可以使用 windows.open('url')
的方式打开新窗口进行下载。
但如果这个文件浏览器是读得懂的,比如 .txt
文件,那浏览器就不会执行下载,而是会直接在页面中把文件内容展示出来。
此时就可以使用 StreamSaver.js
来解决这个问题。
使用 StreamSaver.js
下载文件的大概流程是这样的(为了方便理解,我用一些不专业的术语进行描述):
- 创建一个文件,该文件支持写入操作。
streamSaver.createWriteStream('文件名.后缀')
。 - 使用
fetch
方法访问文件的url,将内容一点点的放到StreamSaver
创建的文件里。 - 监听文件内容是否读取完整,读取完就执行“保存并关闭文件”的操作。
根据上面的指引编写代码:
<!-- 下载按钮 -->
<button id="download">下载</button><!-- 引入StreamSaver.js -->
<script src="../StreamSaver.js"></script>
<script>// 监听按钮点击事件,点击就下载文件download.onclick = () => {// 【步骤1】创建一个文件,该文件支持写入操作const fileStream = streamSaver.createWriteStream('test.txt') // 这里传入的是下载后的文件名,这个名字可以自定义// 【步骤2】使用 fetch 方法访问文件的url,将内容一点点的放到 StreamSaver 创建的文件里fetch('http://localhost:9988/public/test.txt').then(res => {const readableStream = res.bodyif (window.WritableStream && readableStream.pipeTo) {return readableStream.pipeTo(fileStream).then(() => console.log('完成写入'))}// 【步骤3】监听文件内容是否读取完整,读取完就执行“保存并关闭文件”的操作。window.writer = fileStream.getWriter()const reader = res.body.getReader()const pump = () => reader.read().then(res => res.done? writer.close(): writer.write(res.value).then(pump))pump()})}
</script>
大概就是这样子了。
🔔提示
如果遇到提示跨域的问题,可以配置 mitm
指向 mitm.html
。
mitm.html
在 StreamSaver.js仓库
里可以获取到。
可以把 mitm.html
放到你服务器再配置。
streamSaver.mitm = 'https://你的服务器地址/mitm.html'
打包下载 zip
如果想将多个文件打包成zip下载到本地,可以将 StreamSaver.js
和 zip-stream.js
结合在一起使用。
zip-stream.js
在 StreamSaver.js
的仓库里有。
zip-stream.js
在 /examples
目录里。
使用 npm
安装 streamsaver
也能在 /examples
目录下找到 zip-stream.js
,直接引入即可。
import 'node_modules/streamsaver/examples/zip-stream.js'
如果使用 CDN
的方式就直接用 <script src='zip-stream.js'></script>
引入即可。
打包zip下载的步骤:
- 创建下载后的文件名和文件格式。
- 使用
zip-stream
创建一个ZIP
实例,用来不断接收要下载的文件。 - 所有文件下载完成就执行
close()
方法将所有文件真正打包成一个zip
。
<button id="download">下载</button><script src="../StreamSaver.js"></script>
<script src="zip-stream.js"></script>
<script>// 要下载的文件地址列表let urls = [{fileName: 'test.txt',url: 'http://localhost:9988/public/test.txt',},{fileName: 'test.csv',url: 'http://localhost:9988/public/test.csv',}]download.onclick = () => {// 【步骤1】const fileStream = streamSaver.createWriteStream('test.zip')// 【步骤2】const readableZipStream = new ZIP({async pull(ctrl) {for (let i = 0; i < urls.length; i++) {const res = await fetch(urls[i].url)const stream = () => res.bodyconst name = urls[i].fileNamectrl.enqueue({ name, stream }) // 不断接收要下载的文件}// 【步骤3】ctrl.close()}})if (window.WritableStream && readableZipStream.pipeTo) {return readableZipStream.pipeTo(fileStream).then(() => console.log('下载完了'))}}</script>
点击下载按钮后的效果:
这个例子准备了 .csv
和 .txt
文件。下载时会合并成 .zip
,解压后能看到里面的所有文件都是正常能打开的 。
合成文件再下载
在这个例子中,我要将2个 .csv
文件合并成1个再下载。
我准备了两个 .csv
文件(test1.csv 和 test2.csv),它们的内容分别长这个样子。
我要将它们合并成这样子:
在合并文件之前我们首先要清楚这个文件的内容是如何组成的。
在 Excel 中打开 .csv
的每个单元格的内容转换成文本形式的话是用逗号分隔。
如果要合并多个 .csv
文件,只需监听到每个 .csv
下载完成,然后再拼接一个 \n
换行,再下载下一个 .csv
文件即可。
整理一下就是以下几个步骤:
- 拿到一组下载地址,把它们转存到一个迭代器里。
- 递归执行迭代器,如果迭代器里还有内容,就使用
fetch
请求数据。 - 如果迭代器没内容了,使用
writer.close()
关闭文件写入。
该功能写成真正的代码如下所示:
<button id="download" οnclick="down()">下载</button><script src="../StreamSaver.js"></script>
<script>// 编码转换方法let encode = TextEncoder.prototype.encode.bind(new TextEncoder)// 准备好要下载的链接const urls = ['http://localhost:9988/public/test1.csv','http://localhost:9988/public/test2.csv']// 迭代器数据let urlsIter = null// 写入方法放到全局中保存let writer = null// 下载按钮点击事件function down() {// 创建一个下载管道,并将下载后的文件命名为 newTest.csvconst fileStream = streamSaver.createWriteStream('newTest.csv')// 创建写入方法writer = fileStream.getWriter()// 将要下载的链接转换成迭代器urlsIter = urls[Symbol.iterator]()// 开始执行循环下载forDown()}// 循环下载的方法async function forDown() {// 获取迭代器最新一条数据let urlIter = urlsIter.next()// 如果迭代器没数据,执行写入完成操作,并停止递归if (urlIter.done) {writer.close()return}// 迭代器有内容时执行请求操作await fetch(urlIter.value).then(res => {// 通过请求文件url获取到的数据const readableStream = res.bodyif (window.WritableStream && readableStream.pipeTo) {const reader = readableStream.getReader()// 讲获取到的每一包写入文件里const pump = () => {return reader.read().then(readRes => {if (readRes.done) { // 当前文件读取完成后执行// 文件读取完成后换行writer.write(encode('\n'))// 执行请求下一个文件forDown()} else { // 文件读取过程执行// 一包包写入writer.write(readRes.value).then(pump)}})}pump()}})}
</script>
这个案例稍微复杂一丢丢,建议跟着手敲一遍。
推荐阅读
除了本文所讲内容外,StreamSaver.js
的仓库还有好几个实用的例子,有需要的工友可以去看看。
👍《前端中文汉字转拼音》
👍《提升日期处理效率:day.js 实战经验分享》
👍《眨个眼就学会了Pixi.js》
👍《OpenLayers.js 入门教程:打造互动地图的入门指南》
👍《物理世界的互动之旅:Matter.js入门指南》
👍《p5.js 光速入门》
点赞 + 关注 + 收藏 = 学会了 代码仓库
相关文章:

StreamSaver.js入门教程:优雅解决前端下载文件的难题
本文简介 点赞 关注 收藏 学会了 本文介绍一个能让前端优雅下载大文件的工具:StreamSaver.js ⚡️ StreamSaver.js GitHub地址⚡️ 官方案例 StreamSaver.js 可用于实现在Web浏览器中直接将大文件流式传输到用户设备的功能。 传统的下载方式可能导致大文件的加…...

element-ui vue2 iframe 嵌入外链新解
效果如图 实现原理 在路由中通过 props 传值 {path: /iframe,component: Layout,meta: { title: 小助手, icon: example },children: [{path: chatglm,name: chatglm,props: { name: chatglm,url: https://chatglm.cn },component: () > import(/views/iframe/common),me…...

win10 + VS2017 编译libjpeg(jpeg-9b)
需要用到的文件: jpeg-9b.zip win32.mak 下载链接链接:https://pan.baidu.com/s/1Z0fwbi74-ZSMjSej-0dV2A 提取码:huhu 步骤1:下载并解压jpeg-9b。 这里把jpeg-9b解压到文件夹"D:\build-libs\jpeg\build\jpeg-9b" …...

如何实现公网远程桌面访问Ubuntu?VNC+cpolar内网穿透!
文章目录 前言1. ubuntu安装VNC2. 设置vnc开机启动3. windows 安装VNC viewer连接工具4. 内网穿透4.1 安装cpolar【支持使用一键脚本命令安装】4.2 创建隧道映射4.3 测试公网远程访问 5. 配置固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址5.3 测试…...
SpringMvc接收参数
接受参数:1.路径设置RequestMapping(value"地址",method"请求方式") 类|方法GetMapping PostMapping 方法2.接受参数[重点]param直接接收---handler(类型 形参名) 形参名请求参数名注解指定---handler(RequestParam(name"请求参…...

计算机网络文章荟萃
脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?-网络编程/专项技术区 - 即时通讯开发者社区! 1.什么是 socket - 掘金2.socket 的实现原理 - 掘金本文讲述了 socket 在 linux 操作系统下的数据结构,以及阻塞 IO 利用…...

C# Socket通信从入门到精通(4)——多个异步TCP客户端C#代码实现
前言: 在之前的文章C# Socket通信从入门到精通(3)——单个异步TCP客户端C#代码实现我介绍了单个异步Tcp客户端的c#代码实现,但是有的时候,我们需要连接多个服务器,并且对于每个服务器,我们都有一些比如异步连接、异步发送、异步接收的操作,那么这时候我们使用之前单个…...
GitHub为自己的仓库(Repository)设置默认代码缩进(tabsize)
无意中发现GitHub默认显示tab为8个空格的大小,十分不适,故想改成四个字节的缩进 流程 GitHub是支持EditorConfig的。所有只需在Repository根目录下(注意不是.git文件夹下)新建文件 .editorconfig vim .editorconfig内容如下 # top-most EditorConfig…...

Tomcat的动静分离
一、动态负载均衡 3、台虚拟机模拟: 代理服务器:51 tomcat动态页面:53,54 关闭防火墙和安全机制 配置代理服务器,由于做的是七层代理,所以要在http模块配置 配置前端页面 <!DOCTYPE html> <html> <…...

Chimera:混合的 RLWE-FHE 方案
参考文献: [HS14] S. Halevi and V. Shoup. Algorithms in HElib. In Advances in Cryptology–CRYPTO 2014, pages 554–571. Springer, 2014.[HS15] S. Halevi and V. Shoup. Bootstrapping for HElib. In Advances in Cryptology–EUROCRYPT 2015, pages 641–6…...

MySQL 连接出现 Authentication plugin ‘caching_sha2_password的处理方法(使用第二种)
出现这个原因是mysql8 之前的版本中加密规则是mysql_native_password,而在mysql8之后,加密规则是caching_sha2_password, 解决问题方法有两种,一种是升级navicat驱动,一种是把mysql用户登录密码加密规则还原成mysql_native_password. 1. 升级MySQL版本 较早的MySQL版本可能不…...

设置Ubuntu 20.04的静态IP地址(wifi模式下)
一、引言 自己家用的Ubuntu的,重启后ip地址经常会改变,这个时候就需要我们手动配置静态IP了。 二、优点 给Ubuntu设置一个静态IP地址有以下几个好处: 持久性:静态IP地址是固定不变的,与设备的MAC地址绑定。这意味着…...

Qt界面实现中英文切换
要实现的效果,是下拉列表切换中文和English实现按钮文本中英文内容切换。 实现步骤: 1.在VS中鼠标对Translation Files文件右击,选择“添加”--->“模块”. 在弹窗的窗口中选择“Qt”--->“Qt Translation File”。 添加Translation_e…...

Python 编写确定个位、十位以上方法及各数位的和程序
Python 编写确定数字位方法 Python 编写确定个位、十位Python 编写确定个位、十位、百位方法解析:Python 各数位的和程序 利用%(取余符号)、//(整除)符号。 Python 编写确定个位、十位 num 17 a num % 10 b num /…...

AI 引擎系列 1 - 从 AI 引擎工具开始(2022.1 更新)
AI 引擎系列 1 - 从 AI 引擎工具开始(2022.1 更新) AI 引擎系列简介 在这篇题为 Versal 自适应 SoC AI 引擎入门的文章中,我介绍了一些 Versal™ 自适应 SoC 器件中存在的 AI 引擎 (AIE) 阵列。本系列是全新的 AI 引擎系列博文,我…...

Android Kotlin 协程初探 | 京东物流技术团队
1 它是什么(协程 和 Kotlin协程) 1.1 协程是什么 维基百科:协程,英文Coroutine [kəru’tin] (可入厅),是计算机程序的一类组件,推广了协作式多任务的子程序,允许执行被…...

MySQL-DQL【数据查询语言】(图码结合)
作者:chlorine 专栏:数据库_chlorine5的博客-CSDN博客 MySQL——DDL:DDL ——数据定义语言 MySQL——DML:DML——数据操作语言 目录 一.DQL的定义 二.DQL—语法 三.DQL—基础查询(SELECT.. FROM) 👉查询多个字段 👉设置别名 …...
HTTP 响应状态码介绍
HTTP 响应状态码用来表明特定 HTTP 请求是否成功完成。响应被归为以下五大类: 1xx 信息响应 (100–199) 2xx 成功响应 (200–299) 3xx 重定向消息 (300–399) 4xx 客户端错误响应 (400–499) 5xx 服务端错误响应 (500–599)1xx 信息响应 100 Continue 这个临时响应…...
vtk 绘制等高线
使用 VTK 也可以实现等高线绘制,在地图、气象、地质等领域中,经常需要将数据以多个等高线的形式进行展示。在VTK(Visualization Tookt) 中,有专门的AP何以支持简便地实现这一功能。 我们还可以根据算法操作的数据类型或生成的数据类型对算法…...

N-129基于springboot,vue学生宿舍管理系统
开发工具:IDEA 服务器:Tomcat9.0, jdk1.8 项目构建:maven 数据库:mysql5.7 系统分前后台,项目采用前后端分离 前端技术:vuevue-element-admin 服务端技术:springboot,mybatis…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么?它的作用是什么? Spring框架的核心容器是IoC(控制反转)容器。它的主要作用是管理对…...