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…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

android RelativeLayout布局
<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...
《Offer来了:Java面试核心知识点精讲》大纲
文章目录 一、《Offer来了:Java面试核心知识点精讲》的典型大纲框架Java基础并发编程JVM原理数据库与缓存分布式架构系统设计二、《Offer来了:Java面试核心知识点精讲(原理篇)》技术文章大纲核心主题:Java基础原理与面试高频考点Java虚拟机(JVM)原理Java并发编程原理Jav…...

STM32标准库-ADC数模转换器
文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”:输入模块(GPIO、温度、V_REFINT)1.4.2 信号 “调度站”:多路开关1.4.3 信号 “加工厂”:ADC 转换器(规则组 注入…...

轻量级Docker管理工具Docker Switchboard
简介 什么是 Docker Switchboard ? Docker Switchboard 是一个轻量级的 Web 应用程序,用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器,使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...
js 设置3秒后执行
如何在JavaScript中延迟3秒执行操作 在JavaScript中,要设置一个操作在指定延迟后(例如3秒)执行,可以使用 setTimeout 函数。setTimeout 是JavaScript的核心计时器方法,它接受两个参数: 要执行的函数&…...

理想汽车5月交付40856辆,同比增长16.7%
6月1日,理想汽车官方宣布,5月交付新车40856辆,同比增长16.7%。截至2025年5月31日,理想汽车历史累计交付量为1301531辆。 官方表示,理想L系列智能焕新版在5月正式发布,全系产品力有显著的提升,每…...