图片自动压缩

更新日期: 2019-07-18阅读: 2.8k标签: 压缩

团队开启了一个新项目,希望能在原来项目的工程化基础上再进一步,于是想到了图片自动压缩。这里的图片自动压缩并不是在webpack构建阶段压缩,而是在git commit的时候进行。


pre-commit

pre-commit 是git hook 众多钩子中的一个,在每次 git commit 前执行,可以是shell等任何可执行的脚本文件,通过返回0 or 1 来表示commit是否通过。在bash中,非零返回值代表失败.


pre-commit.js

市面上有许多通过node.js来封装pre-commit的npm包,我使用了功能最精简的pre-commit.js , 按README在package.json中简单配置后,可以看到 .git/hooks/pre-commit脚本如下所示:

#!/bin/bash
./node_modules/pre-commit/hook
RESULT=$?
[ $RESULT -ne 0 ] && exit 1
exit 0

pre-commit.js重写了原来的示例钩子,在git commit时执行自己的/hook脚本.


hook bash

# 之前的代码主要确认node环境
# git commit --dry-run 的情况(测试性提交,执行动作不产生结果),不对提交作任何处理
if [[ $* == *--dry-run* ]]; then 
  if [[ -z "$BINARY" ]]; then # 但还是要检查下NODE是否存在,不存在返回非零值代表失败
    exit 1
  fi
else
  # 用NODE 执行 'pre-commit/index.js' 模块
  "$BINARY" "$("$BINARY" -e "console.log(require.resolve('pre-commit'))")"
fi

最终pre-commit/index.js 将读取package.json中配置好的路径来执行指定的脚本。


pre-commit/index.js

// 执行自定义钩子脚本的核心部分
Hook.prototype.run = function runner() {
  var hooked = this;

  (function again(scripts) {
    // 脚本数组为空,则返回0,执行本次 commit .
    if (!scripts.length) return hooked.exit(0); 
    // 否则弹出一个脚本来执行
    var script = scripts.shift();

    // 使用的是异步执行子进程方法child_process.spawn.
    spawn(hooked.npm, ['run', script, '--silent'], {
      env: process.env,
      cwd: hooked.root,
      stdio: [0, 1, 2]
    }).once('close', function closed(code) {
      // 监听了执行结束的close事件,递归继续执行下一个脚本
      if (code) return hooked.log(hooked.format(Hook.log.failure, script, code));
      
      again(scripts);
    });
  })(hooked.config.run.slice(0));
};

从上述代码中可以看出,即使我们定义的钩子脚本有异步处理逻辑也是可以的,因为整个脚本都是新开了一个异步的子进程来执行的,通过监听close事件来确认异步逻辑执行完毕,最终exit(0)通知成功,执行commit。


TinyPNG

TinyPNG是一个专门进行有损压缩PNG图像的网站,经过它特有的算法,能够降低图像70% - 80%的大小。TinyPNG提供在线api,每月500张免费压缩额度,可以通过npm包tinify.js调用。

const execSync = require('child_process').execSync;
const path = require('path');
const tinify = require('tinify');
tinify.key = 'tDvJPN2Fd4yjtHJDSwQQtQQZWTldVls7'; // tinypng提供的用户key

console.log('pre-commit hook start! \n');
let diff = getDiffFiles();
compressPics(diff);

function getDiffFiles(type) {
  // pre-commit钩子本身不传递参数
  //https://git-scm.com/docs/githooks/1.7.4#_pre_commit
  // 所以通过git diff 命令拿到本次提交涉及的变动文件
  let root = process.cwd();
  let files = execSync('git diff --cached --name-status HEAD').toString().split('\n');
  let result = [];
  // add, delete, modified, renamed, copied
  type = type || 'admrc';
  let types = type.split('').map(t => {
    return t.toLowerCase();
  });
  files.forEach(file => {
    if (!file) {
      return;
    }
    let temp = file.split(/[\n\t]/);
    let status = temp[0].toLowerCase();
    let filePath = root + '/' + temp[1];
    let extName = path.extname(filePath).slice(1);
    
    if (types.length && ~types.indexOf(status)) {
      result.push({
        status: status, // admrc中的一个
        path: filePath, // 绝对路径
        subpath: temp[1], // 相对路径
        extName: extName, // 扩展名
      })
    }
  });
  return result;
}

function compressPics(files) {
  let pngs = files.filter(file => file.extName === 'png' && ['a', 'm'].includes(file.status));
  console.log(pngs);
  pngs.forEach(file => {
    let source = tinify.fromFile(file.subpath);
    source.toFile(file.subpath)
    .then(res => {
      ++flag;
      console.log(file.subpath + ' has been compressed !');
      execSync('git add .', {encoding: 'utf8'});
    })
    .catch(err => {
      console.log(err);
      process.exit(1);
    })
  })
}

该方法使用了带key的在线接口,而且大大延长commit时长,还受到每月500张限制。


imagemin-pngquant.js

鉴于tinipng的缺点,我换了一个npm包,使压缩过程能够在本地完成。

const imagemin = require('imagemin');
const imageminPngquant = require('imagemin-pngquant');

let parentFolder = {};
  pngs.forEach(x => { // 根据不同父级目录分类
    let pf = x.subpath.slice(0, x.subpath.lastIndexOf('/'));
    parentFolder[pf] ? parentFolder[pf].push(x.subpath) : parentFolder[pf] = [x.subpath];
  });

  for (let pf in parentFolder) {
    imagemin(parentFolder[pf], { // 原图片目录
      destination: pf,      // 生成图片的目录
      plugins: [
        imageminPngquant({
          speed: 1,
          quality: [0.40, 0.50], 
        })
      ]
    })
    .then(res => {
      execSync('git add . ');
    })
    .catch(err => {
      console.log(err);
      process.exit(1);
    })
  }

实际检验在上述配置下,imagemin-pngquant具有80%的压缩率,高于tinypng,唯一的例外是一张色彩空间为RGB的图片,这张图片在同批测试中仅得到了20%的压缩率,在tinyPNG中虽然获得了50%的压缩率,但也属于同批图片中效率最低的一张。

因此在UI交付图片时,我们应该关注下色彩空间是否为sRGB,如果不是,应该打回重新切。正常情况下,用于Web程序的图片,颜色通道应为sRGB,因为绝大部分浏览器是使用sRGB色彩空间来渲染图片的。
Why Should We Use sRGB in Broswer
Preparing Images Color for the Web
除了压缩效率压过tinyPNG一头,在时间上iamgemin-pngquant几乎是秒转换,同批图片通过tinyPNG的api转换需要花费10 - 20s。


webp

将png格式的图片转换为webp格式,可以节约更多的空间,在lighthouse性能检测中拿到更高的得分,但IOS系统目前还是无法直接兼容它,需要做转换,很难在工作流中做自动化的处理。


链接: https://www.fly63.com/article/detial/4790

JS/TS语言zip压缩解压缩三方库及其使用

TS语言的Laya项目里,要用typescript对压缩过的文件解压缩。文件是AS3写的工具导出的。下载找到的zlib.js或zlib.min.js文件,自己编写zlib.min.d.ts文件,代码如下:

Apache开启GZIP压缩功能方法

Gzip是一种流行的文件压缩算法,现在的应用十分广泛,尤其是在Linux平台,Gzip在现代浏览器中默认都是支持的,当一个纯文本文件使用Gzip压缩可以减少70%以上的文件大小,本文讲解了如何开启Apache平台上的Gzip压缩功能

webpack 代码压缩

正常情况下,如果我们配置生成环境,webpack会自动帮js压缩,必须配置--mode=production,但是如果我们要把css也压缩的话,问题就来了,css压缩

PHP 实现压缩解压操作

在php中,有时我们需要使用到压缩文件操作,压缩文件可以节省磁盘空间;且压缩文件更小,便于网络传输,效率高,下面我们就来了解php的压缩解压相关操作

HBase 有几种压缩方式?

在使用 HBase 的时候,为了节省数据的空间占用,通常我们会为 HBase 设置压缩属性。与其他的表级别属性一样,压缩设置也是针对具体列族进行设置的。HBase 创建表时默认压缩为 NONE ,即没有压缩,除非指定

使用Canvas压缩图片

讲干货,不啰嗦,当涉及对图片有质量压缩要求的时候,可以使用Canvas实现图片压缩。步骤:1.获取img元素,既要压缩的图片2.创建canvas对象3.使用canvas的drawImage方法绘制图片

js 压缩图片(只缩小体积,不更改图片尺寸)

我们知道现在,手机拍照在10MB左右,体积太大,在上传到服务器前,能不能只缩小图片的体积,而不改变图片尺寸? 这里我们可以通过canvas实现对图片的重新绘制,已便达到图片压缩的效果。

利用 canvas 实现图片压缩

项目中做身份证识别时,需要传送图片的 base64 格式编码,但是手机拍摄的照片都太大了,转成 base64 简直可怕,因此找了一下解决办法,input 标签的 onchange 事件是在上传完文件之后触发。

Nginx优化:网页压缩与缓存时间

Nginx的ngx http .gzip_ module压缩模块提供对文件内容压缩的功能,允许Nginx服务器将输出内容在发送客户端之前进行压缩,以节约网站带宽,提升用户的访问体验,默认已经安装

javascript 代码是如何被压缩的

随着前端的发展,特别是 React,Vue 等构造单页应用的兴起,前端的能力得以很大提升,随之而来的是项目的复杂度越来越大。此时的前端的静态资源也越来越庞大,而毫无疑问 javascript 资源已是前端的主体资源,对于压缩它的体积至为重要

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!