使用squoosh网的压缩方法实现浏览器前端压缩图片功能

假如说你做的网站有需要用户自定义上传图片的功能,那么往往这些图片,都需要进行压缩,且往往是需要经过服务器端压缩的。同时如果说你不懂图片压缩原理的话,你还得去花钱使用他人的API进行图片压缩(例如tinyPng),熊猫压缩的服务器部署在国外,我们调用其API也存在响应速度慢、丢包等问题。
这时候有人会说,canvas压缩不就完事了吗?的确,使用canvas也能够进行图片压缩,但是博主上手操作之后发现其效果有点糟糕。假如说画布大小与图片大小一致,调低quantity的话,压缩完的图片往往会很模糊;而如果画布大小放大为图片宽高的两倍,图片压缩的效果却不尽人意。
博主原本也通过找API调用来做图片压缩功能,但是市面上大多有提供API功能的图片压缩网站,符合要求的大多需要收费,博主在找的过程中,发现了有几个压缩网站,压缩是不经过后端服务的,也就是前端浏览器来做压缩,且其中的一个网站是开源项目(Squoosh谷歌图片压缩工具),博主灵机一动,想着要不就把人家做好的搬过来借用一下。好了,废话少说这就开始行动!

准备工作

1. 准备工作第一步当然是上github去clone源码到本地,git链接

2. 安装依赖启动项目安装依赖启动项目

3. 分析项目,找出压缩图片相关的方法

该项目是使用TypeScript开发,由于博主使用的项目中没有使用到TypeScript,故找出相应的方法后,需要将其转为JS才能用到博主的项目中。
网站首页如下:
网站首页
通过select an image按钮上传一张图片后,就会进入图片压缩前后的对比界面(前端压缩速度很快,几乎可以说是秒压),如下图:
压缩前后对比图
页面左下角和有下角分别为压缩前后图片大小对比,右下角可以通过Quality滑动条来控制图片的压缩质量(默认75),中轴线可以拖拽查看图片压缩前后的效果图。速度快,可调节质量,可以说这就是博主想要的效果,10k+star的项目就是屌!

4. 通过页面中Quality滑动条找出压缩方法

博主一番分析后,决定从Quality滑动条入手,找出其背后调用的方法:
首先该项目为preact项目(github的star目前为26.9K),写法跟react差不多,是react的轻量级实现,其简介就一句话:“Fast 3kB alternative to React with the same modern API.”,看来博主又多了一个学习目标了噗。
博主作为TypeScript新手,React菜鸟级别选手,定位到了滑动条的change事件的定义位于src/codecs/mozjpeg/options.tsx第27行,其事件最后会调用this.props.onChange(newOptions);调用父组件OptionsonChange事件位于src/components/Options/index.tsx第124行;然后它是作为传入的prods事件去调用其父组件CompressonEncoderOptionsChange私有函数做setState操作,位于src\components\compress\index.tsx第484行。
setState之后,会触发组件内的私有函数updateImage,其会去调用compressImage方法去判断解码类型数据,从而执行相应的压缩方法,最终会执行137行的case,case mozJPEG.type: return processor.mozjpegEncode(image, encodeData.options);
到此,才剖析到了真正的压缩方法Processor类中的mozjpegEncode方法,位于src\codecs\processor.ts第139行,接下来只需要全局搜索mozjpegEncode方法,按照上边一步步地往里面剖析,将相关联的代码抽离出来放到一个文件夹中,结果如下图:
找出压缩方法相关的文件

解析处理该文件夹中的文件

由于博主写的项目中并没有引入TS,同时也不希望因为这个压缩功能而导致项目中混入TS,故博主去除了.d.ts结尾的类型声明文件,再将其编译为JS文件,然后再修改每个文件引用的地址。
接着在该文件夹中新建一个index.js作为该方法的入口文件,对外暴露一个compressImg方法接收饿了么UI上传组件
before-upload事件回调中的file,返回一个压缩成功后的compressFile,代码如下:

import { encode } from './encoder'

// 默认参数配置
let option = {
  quality: 75, // 可调整压缩质量(默认75)
  baseline: false,
  arithmetic: false,
  progressive: true,
  optimize_coding: true,
  smoothing: 0,
  color_space: 3,
  quant_table: 3,
  trellis_multipass: false,
  trellis_opt_zero: false,
  trellis_opt_table: false,
  trellis_loops: 1,
  auto_subsample: true,
  chroma_subsample: 2,
  separate_chroma_quality: false,
  chroma_quality: 75
}

export async function compressImg(file) {
  // 接收file之后需取到宽高,利用canvas绘制得到imageData
  let img = new Image()
  let _URL = window.URL || window.webkitURL
  img.src = _URL.createObjectURL(file)
  let canvas = document.createElement('canvas')
  return new Promise((resolve) => {
    img.onload = async () => {
      canvas.height = img.height
      canvas.width = img.width
      let ctx = canvas.getContext('2d')
      ctx.drawImage(img, 0, 0)
      let imageData = ctx.getImageData(0, 0, img.width, img.height)
      // 调用Squoosh中暴露出来的压缩方法encode
      let result = await encode(imageData, option)
      // 其返回结果为ArrayBuffer类型,将其转为blob类型后再转回file返回到页面中
      let blob = new Blob([result])
      let compressFile = new File([blob], file.name, {
        type: file.type
      })
      resolve(compressFile)
    }
  })
}

到这里就结束了吗?并没有哦,抽离出来的文件中,有一个.wasm结尾的文件,这个wasm文件就相当于windows的dll库,只不过是web上不是windows上的,其压缩是依赖于这个文件的。
但是在前端中并不认识他,需要在对应的webpack配置中利用file-loader或者wasm-loader去解析它(这里请自行配置)。

成果展示

终于到了看成果的时候了,不枉博主费尽心思地借鉴,效果还是挺给力的,不多说了直接上图:
图片压缩效果图
博主上传了一张494KB(size为50W+),压缩完毕后的图片约为100多KB(size为10W+),可以非常明显得看到效果还是非常的明显的,而且压缩的速度非常的快!这里的压缩效果其实跟Squoosh一致,所以小伙伴们想看效果也可以直接上其网站试试【传送门】

结语

本文章作为一篇没有什么技术含量的博文,不是教大家如何去从0做前端图片压缩,而是教的大家投机取巧做搬运工,博主确实心底有点惭愧。
但毕竟博主作为菜鸟前端选手,靠自己来手搓一个压缩图片方法不太现实,待日后博主了解其压缩原理,定会再开一篇博文,分享给大家如何从0搭建!好了,该滚去继续学习了,还是太嫩了。

猜你喜欢

转载自blog.csdn.net/weixin_43905402/article/details/107913072