纯前端 JS 实践打包并下载大文件


theme: channing-cyan

甲方客户甩来一个需求,需要把用户上传的视频、图片打包成zip并下载下来,并且要遵循一定的目录结构。原本需求定为后端实现,但经过后端同学调研了一波,发现会严重消耗后端的性能、带宽,遂卑微(niubi)前端上岗上线解决问题。

在谷歌、必应、百度…上调研了一波前端技术实现,大致找到两种解决方案,一是jszip + file-saver,二是streamSaver,下面介绍下这两种技术的使用。

jszip + file-saver

下载插件,随便附上github地址

jszip:https://github.com/Stuk/jszip
file-saver:https://github.com/eligrey/FileSaver.js

npm install jszip file-saver

jszip + file-saver 使用方式比较简单,如下即可实现资源打包下载:

import JSZip from 'jszip';
import { saveAs } from 'file-saver';

(async() => {
  // 初始化一个zip打包对象
  const zip = new JSZip();

  // 创建一个名为images的新的文件目录
  const folder = zip.folder('images');

  // 请求远程资源的blob,其他文件,比如视频、表格等也是一样的
  const blob = await fetch('https://abc.com/test.png').then(response => response.blob())

  // 文件夹添加资源,图片也支持base64类型 {base64: true}
  folder.file('test.png', blob, { Blob: true });

  // 把打包内容异步转成blob二进制格式
  zip.generateAsync({type:"blob"}).then((content) => {
    // 下载压缩包
    saveAs(content, 'example.zip');
  });
})()

不过该方式有比较明显的缺陷,主要在兼容性及资源的体积上,在谷歌浏览器上能达到 2GB,但在其他浏览器上一般只支持到几百 MB,可参考下面的file-saver浏览器支持介绍:

image.png
所以,为了兼顾非谷歌浏览器的用户,还需要进一步地探索新的技术解决方案,也可以结合本公司对前端兼容性的要求来权衡。

streamSaver

streamSaver 的使用会稍微麻烦一点,不过按照官方的使用示例,也比较容易跑起来。

下载插件

npm install streamsaver

此外,还需要从github上下载几个文件到项目目录,分别是:

mitm.html:https://github.com/jimmywarting/StreamSaver.js/blob/master/mitm.html

sw.js:https://github.com/jimmywarting/StreamSaver.js/blob/master/sw.js

zip-stream.js:https://github.com/jimmywarting/StreamSaver.js/blob/master/examples/zip-stream.js

streamSaver 的使用方式如下:

import streamSaver from 'streamsaver';
// gihub上下载的zip-stream.js
import '@/libs/streamsaver/zip-stream';

// mitm.html的存放目录,sw.js同级存放,都需要跟随项目部署到服务器
streamSaver.mitm = `${location.origin}/libs/streamsaver/mitm.html`;

// 初始化打包对象
const readableZipStream = new window.ZIP({
    async pull(ctrl) {
        // 请求资源 & 设置目录
        const res = await fetch('https://abc.com/test.png');
        const stream = () => res.body;
        const name = '/images/test.png';

        // 添加到处理队列,可遍历添加
        ctrl.enqueue({ name, stream });

        ctrl.close();
    },
});

// 资源命名
const fileStream = streamSaver.createWriteStream('example.zip');

// more optimized
if (window.WritableStream && readableZipStream.pipeTo) {
  return readableZipStream
    .pipeTo(fileStream)
    .then(() => {
      console.log('下载成功')
    })
    .catch((error) => {
      console.error('下载失败', error);
    })
}

// less optimized
const writer = fileStream.getWriter();
const reader = readableZipStream.getReader();
const pump = () =>
  reader
    .read()
    .then(() => {
      console.log('下载成功')
    })
    .catch((error) => {
      console.error('下载失败', error);
    })

pump();

经过简单的测试发现,streamSaver 的兼容性更佳,在一些非谷歌浏览器上,也能达到 2GB体积的下载,所以也是目前实现需求的首选方案。

其他注意事项

  1. 不同的浏览器支持度不一样,需要多测试,并且考虑客户的接受程度;
  2. streamSaver 在本地因为证书问题,无法正常下载资源,可发布到测试服务器再验证;
  3. 资源跨越问题需要自行解决。

本文正在参加「金石计划 . 瓜分6万现金大奖」

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容