纯前端如何原生处理数据压缩

随着前端技术的快速发展,在浏览器里能做的事情越来越多,我们的web应用也变得越来越复杂,处理的数据量越来越大,很多之前需要交给服务端或者需要其他语言编写的库再编译成JS使用,亦或者有些只能靠第三方的库来实现的功能,现在我们逐渐的可以直接使用浏览器自带的API来实现了。

今天给大家介绍的一个场景是前端压缩(如:gzip压缩),利用浏览器自带的CompressionStreamDecompressionStream以Stream流的形式来做压缩解压缩操作,使用这些Web API配合流的好处是:

  1. 减少第三方库的依赖,提升页面加载速度,顺便避免三方库因安全漏洞带来的风险
  2. 原生API理论上能带来更好的运行性能
  3. 使用流可以大大减少内存占用,对于大批量数据可以做到按需处理

关于前端Stream流的更多指南,可参考之前的文章:Node与Web Streams完全指南

我们在前端看到压缩比较多的地方是静态资源的gzip压缩,这种一般由服务端压缩或打包时直接压缩相关资源,然后传给前端,浏览器拿到数据后根据http头里的压缩算法信息自动解压缩,对页面JS无感知。

但是当我们需要上传大量数据时,为了避免数据过大时间过长,我们就需要先把数据压缩后再上传给服务端,或者仅仅是为了做一些混淆,不让人轻易地看到传输内容?;我们也可以做本地文件的压缩,或者在需要提供用户本地下载数据的情形下,前端处理压缩后直接下载文件至本地文件夹等场景。

由于压缩解压缩可能会处理非常大的数据,所以这个能力一般都是基于流的形式,不管是Web Stream还是Node.js Stream。

fetch下载文件至本地

在Web端最早提供ReadableStream可读流的地方就是fetch的Response里的body属性(如:res.body),你甚至可以用fetch来做个大文件下载,比如:

async function downloadMovie() {
  // https://developer.mozilla.org/en-US/docs/Web/API/window/showSaveFilePicker  

  const newHandle = await window.showSaveFilePicker({

    suggestedName: `movie.mp4`,
  });

  // 得到writableStream用来写进上面选择/新建的文件
  const writableStream = await newHandle.createWritable();

  // 开始下载
  const res = await fetch('movie.com/123')
  await res.body
           // 可读流调用pipeTo把数据传到可写流里   
           .pipeTo(writableStream)
  console.log('下载完成')
}

下载压缩文件,实时解压缩保存至本地

我们也可以请求一个压缩的文件,直接解压缩并保存至本地:

async function downloadAndDecompressAndSave() {
  // https://developer.mozilla.org/en-US/docs/Web/API/window/showSaveFilePicker  

  const newHandle = await window.showSaveFilePicker({

    suggestedName: `test.js`,
  });

  const writableStream = await newHandle.createWritable();
  // 开始下载
  const res = await fetch('cdn.com/test.js.gz')
  await res.body
           // pipeThrough用来写到转换流,并返回该转换流的可读流,也就是输出解压缩后的数据
           .pipeThrough(new DecompressionStream('gzip'))   
           // 可读流调用pipeTo把数据传到可写流里   
           .pipeTo(writableStream)
  console.log('下载完成')
}

本地文件压缩下载

我们也可以用这些API做一个简单的网页版压缩工具,用户通过选择一个本地的文本文件,通过CompressionStream转换流做压缩处理,并实时写到一个新的文件里,如:

<input type='file' onchange='onFileChange(...arguments)'>
async function onFileChange(e) {
  const file = e.target.files[0]
  // 写文件流
  const newHandle = await window.showSaveFilePicker({
    suggestedName: `${file.name}.gz`,
  });
  const writableStream = await newHandle.createWritable();

  // File的stream()返回一个该文件的可读流
  await file.stream()
    // pipeThrough用来写到转换流,并返回该转换流的可读流,也就是输出压缩后的数据
    .pipeThrough(new CompressionStream('gzip'))
    // 压缩后的数据直接pipe给新文件的可写流
    .pipeTo(writableStream)
  console.log('压缩完成')
}

以下为GIF示例:

gzip-demo.gif

兼容性

以上为利用浏览器原生WEB API处理纯前端压缩解压缩的一些应用场景,在浏览器兼容的情况下建议WEB API走起。

CompressionStreamDecompressionStream: Chrome >= 80

上面的window.showSaveFilePicker是试验性API, Chrome >= 86

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYykbkRS' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片