streamsaver——下载打包2GB以上的文件

11,585 阅读4分钟

下载打包2GB以上的文件

本次记录的便是由客户端采用streamsaver.js用的“流”的方式来下载并打包。我所在的公司是在区块链公证相关业务,涉及到录像、音频、文本、pdf等多类文件的存储并上链,所以当证据文件多了法院的公证员下载证据要一个一个的去下载,操作不太方便,所以便提供了打包全部下载功能。

解决方案预研

FileSaver.js

网上常见的方式是 FileSaver.js + JSZIP 采用的是将先将文件下载的本地,然后该插件将客户端内存存储中的文件给导出并打包.但是这插件有很大的限制就是文件大小限制,谷歌浏览器都只能2GB以下,其他更低。因为浏览器的内存非常有限,所以就不考虑了

StreamSaver.js

StreamSaver.js采用直接创建一个可写流到文件系统的方法。而不是将数据保存在客户端存储或内存中,对于在浏览器操作流的资料大家可以看到一下这篇文章以流的角度处理网络请求 对于需要保存在客户端创建的大量数据的操作来说是完美的解决方案。

有了读写流的能力,还需要解决的一个问题就是如何维持一个持续的通道,保证能够一边在下载,一边把下载的东西写到本地。制造一个(不存在的)下载链接,然后用 Service Worker 去拦截对这个下载链接的请求,持续往这下载链接写入文件流就可以了。

Service worker 的概念

1.Service worker是一个注册在指定源和路径下的事件驱动worker。它采用JavaScript控制关联的页面或者网站,拦截并修改访问和资源请求,细粒度地缓存资源。你可以完全控制应用在特定情形(最常见的情形是网络不可用)下的表现。

2.Service worker运行在worker上下文,因此它不能访问DOM。相对于驱动应用的主JavaScript线程,它运行在其他线程中,所以不会造成阻塞。它设计为完全异步,同步API(如XHR和localStorage)不能在service worker中使用。

3.出于安全考量,Service workers只能由HTTPS承载,毕竟修改网络请求的能力暴露给中间人攻击会非常危险。在Firefox浏览器的用户隐私模式,Service Worker不可用。

环境安装

》》》vue2.x项目《《《

streamsaver

npm install streamsaver --save

zip-stream.js

下载地址:github下载地址 它是用于处理下载地址并启动下面两个文件的核心文件

静态资源

把以下文件存储在项目目录/public 与index.html同级

  • 1.mitm.html
    伪造服务器的文件 ,它相当于伪造一个服务器来下载你的文件并伪造一个新的下载地址, 同时利用下面的serviceWorker以文件流的形式写入新的下载地址

文件下载地址:github下载地址

  • 2.serviceWorker.js 将下载的内容处理成文流

文件下载地址:github下载地址

方法实现

testPage.vue

<template>

 <button @click="downloadAll"></button>
  
</template>

引入部分

import streamSaver from 'streamsaver'
//引入我们上面提到过的文件
import './js/zip-stream.js'
//修改插件中的默认值改为自己本地(发布到线上记得改为线上地址)
streamSaver.mitm = 'http://localhost:3010/mitm.html' 

方法部分

  methods: {
    downloadAll (){
      let data = [ 
        ['文件01','https://xxxx:443.com/xxxx01'],
        ['文件02','https://xxxx:443.com/xxxx02'],
        ['文件03','https://xxxx:443.com/xxxx03']
      ]
      let urlArr = data.values //迭代对象
      
      let fileStream = streamSaver.createWriteStream(`全部文件.zip`)//打包出来的文件
      
      /*import ./zip-stream.js文件以后window.ZIP这个方法*/
      
      let readableZipStream = new window.ZIP({ 
        pull (ctrl) {
          const it = urlArr.next()
          if (it.done) {//迭代终止
              ctrl.close()
          } else {
              const [name, url] = it.value
              return fetch(url).then(res => {
                  ctrl.enqueue({
                      name,
                      stream: () => res.body
                  })
              })
          }
        }
      })
      if (window.WritableStream && readableZipStream.pipeTo) {
        return readableZipStream.pipeTo(fileStream)
        .then(() => {
          console.log('下载成功')
         })
        .catch(()=>console.log'网络不佳,下载失败'))
      }
    }
  }
}

结语

上述问题是打包解决方案,很细节没写出来,比如终止下载等方法
还有更多解决方案可以streamsaver的gitHub中的Examples

  1. Saving audio or video stream using mediaRecorder
  2. Piping a fetch response to StreamSaver
  3. Write as you type
  4. Saving a blob/file
  5. Saving a file using webtorrent
  6. Saving multiple files as a zip
  7. slowly write 1 byte / sec 很少写这方面的文章,文章有什么问题,欢迎在评论区说出你的想法