🎀用流式写入处理大文件下载
介绍
流式写入直接将数据流写入用户磁盘,避免传统方法(如Blob
或URL.createObjectURL
)因内存限制导致的大文件下载问题。StreamSaver.js
库使用浏览器原生的 Streams API,逐块写入数据到磁盘。通过 Service Worker
和中间人(MITM
)技术,模拟服务器响应,绕过浏览器对下载文件大小的限制。而且,不需要服务端做任何修改。
使用
安装:
1 | npm i streamsaver |
使用:
1 | import { createWriteStream } from 'streamsaver'; |
原理
- 用户点击下载按钮;
- 动态创建一个隐藏的iframe,加载MITM脚本;
- MITM脚本在iframe中注册Service Worker,并声明其作用域;
- 主页面和iframe通过postMessage通信,传递数据快;
- Service Worker接收数据,通过流式API写入本地文件;
浏览器要求文件下载必须由用户主动触发,如点击事件。iframe的创建和MITM脚本的加载会在用户点击事件的同步上下文中完成。这样,后续通过iframe触发的下载操作仍然被视为用户手势的延续,避免被浏览器阻止。
浏览器默认禁止脚本直接操作本地文件系统,且下载操作通常需要与当前页面同源。该iframe的源被设置为一个独立的、与主页面不同的虚拟URL,从而创建一个“隔离的上下文”。这个隔离的上下文可以绕过主页面的一些安全策略,允许直接与Service Worker通信并触发下载。
Service Worker需要注册在特定的作用域下,且通常需要与页面同源。iframe中加载的MIMT脚本会动态注册一个Service Worker,并控制其作用域。通过将Service Worker隔离在iframe中,可以避免与主应用的Service Worker冲突,同时确保下载逻辑的独立性。
主页面通过postMessage向iframe发送数据库,iframe中的MIMT脚本将数据转发给Service Worker,Service Worker将数据流式写入磁盘。
缺点
因为通过iframe处理下载逻辑,生产环境需要使用https
,否则会有不安全混合内容限制。
如果不是https
,则 StreamSaver
会改用popup
,下载时页面左上角会有小弹窗闪现。
更好的方法
如果不考虑浏览器兼容性,可以用这个方法。
1 | try { |
总结
streamsaver
最好在https环境中使用,getReader
可能不兼容旧浏览器。streamsaver
先选择保存位置再下载,getReader
先下载再选择保存位置。