在线图片压缩

负责项目的朋友甩给我一个在线图片压缩网站(https://tinypng.com)

从“图片压缩”,看程序员的自我成长

tinypng

意图很明显,但难道让我把图片一张张上传上去压缩下载吗?

我依然不是前端小白,即使肉眼一扫基本知道哪些图片需要处理,但这种重复劳动虽然管用但很“不健康”,对于自我成长,首先要有强烈的意愿去杜绝这种最低效的劳动。

我尝试去找网站相关的开发者栏目,但觉得麻烦,又要申请 key,又要 curl 调链接:

从“图片压缩”,看程序员的自我成长

 

所以换我们前端比较熟悉的 gulp 入手。

gulp.js 脚本

项目是基于 vue-cli 的骨架,不想加入 webpack 配置增加复杂度,所以就回归到简单上手的 gulp 。

因为我们是有经验的开发(百度一下),所以很快就找到能用的模块:gulp-image

const gulp = require('gulp'); 
const image = require('gulp-image'); 
  
gulp.task('image', function () { 
  gulp.src('./fixtures/*') 
    .pipe(image()) 
    .pipe(gulp.dest('./dest')); 
});

当然如果嫌文档不太友好,也可以试下 gulp-imagemin。

具体如何使用就暂且省略,因为这些不是本文重点。

很好,我们不再 人工处理 这些问题,有了些“经验”的提升,但依然停留在使用 API 层面,任何一个人都能代替我们。

那么第二步,请在工作中 抽出时间来思考这些工具是怎么解决问题的,而不是止于寻找工具,调用它们完事。

自定义脚本

试问自己这些问题:

如果没有这些工具怎么办?我们肯定要自己写脚本。

但我们又不了解 png、jpg 之类格式的压缩算法,那怎么办?

这里就要提到,大多数“核心”的模块基本都被人实现过一遍了,而我们常用的工具都是对这些模块的再次封装,就好比小米手机对各个厂商的零部件整合一样。

但为什么有些工具那么“出众”呢?我认为就是他们创造了类似 miui 之类的玩意,让我们使用者用起来更加舒服,方便。

确定使用哪些基础库

好,现在开始分析上面 gulp 模块内部调用哪些“核心”库:

gulp-image 相关依赖:

// gulp-image 
"dependencies": { 
    "gifsicle": "^5.0.0", 
    "jpeg-recompress-bin": "^4.0.0", 
    "mozjpeg": "^6.0.1", 
    "pngquant-bin": "^5.0.2", 
    ... 
  }

gulp-imagemin 相关依赖:

// gulp-imagemin 
"dependencies": { 
  "imagemin": "^7.0.0", 
    ... 
}, 
"devDependencies": { 
  "imagemin-pngquant": "^8.0.0", 
  ... 
}, 
"optionalDependencies": { 
  "imagemin-gifsicle": "^7.0.0", 
  "imagemin-mozjpeg": "^8.0.0", 
  "imagemin-optipng": "^7.0.0", 
  "imagemin-svgo": "^7.0.0" 
}

我看了 imagemin-pngquant 和 imagemin-mozjpeg 相关依赖,发现和 gulp-image 用的一样,所以基于这些模块来作为我们自定义脚本的基础。

开始动手

这里我选择了 imagemin 相关的基础模块,还引入 node.js 相关文件操作,

//定义模块 
const imagemin = require('imagemin') 
const imageminJpegtran = require('imagemin-jpegtran') 
const imageminPngquant = require('imagemin-pngquant') 
 
const path = require('path') 
const fs = require('fs')

定义一些基础信息

const imageRoot = path.resolve(__dirname, 'src/assets/images') 
const newImageDirtory = path.resolve(__dirname, 'tiny') 
const parseFilesPromise = []

定义主要方法
// 创建目录 
function createDir(dir) { 
  if (!fs.existsSync(dir)) { 
    fs.mkdirSync(dir) 
  } 
} 
// 读取目录 
function readDirtory(dir, newDir) { 
  createDir(newDir)//创建目录 
  const dirData = fs.readdirSync(dir) 
 
  for (let innerDir of dirData) { 
    const totalDir = path.resolve(dir, innerDir) 
    const childDir = path.resolve(newDir, innerDir) 
    const stat = fs.statSync(totalDir) 
    if (stat.isDirectory()) { 
      // 递归子文件夹 
      readDirtory(totalDir, childDir) 
    } else { 
      // 解析文件 
      if (['.png', '.jpeg'].indexOf(path.extname(totalDir)) !== -1) { 
        parseFiles(totalDir,childDir) 
      } 
    } 
  } 
} 
// 准备解析文件 
function parseFiles(originFile,destFile){ 
  parseFilesPromise.push( 
    imagemin([originFile], { 
      destination: path.dirname(destFile), 
      glob: false, 
      plugins: [ 
        imageminJpegtran(), 
        imageminPngquant({ 
          quality: [0.6, 0.8] 
        }) 
      ] 
    }) 
  ) 
}

最后执行:

readDirtory(imageRoot, newImageDirtory) 
 
Promise.all(parseFiles) 
  .then(data => {}) 
  .catch(err => { 
    console.log(err) 
  })
似乎没什么问题,但在实际中遇到了某些图片太大,使得模块解析报错,导致最后 promise.all break 掉。

优化

存放于 promise 队列中的 promise 对象额外封装一个 promise,用来解析中间出现的 error :

parseFilesPromise.push( 
  new Promise((resolve, reject) => { 
    return imagemin([totalDir], { 
      destination: path.dirname(childDir), 
      glob: false, 
      plugins: [ 
        imageminJpegtran(), 
        imageminPngquant({ 
          quality: [0.6, 0.8] 
        }) 
      ] 
    }) 
      .then(data => { 
        resolve(data) 
      }) 
      .catch(err => { 
        reject(totalDir) 
      }) 
  }) 
)

在 promise.all 中,通过数组的 map 处理异常熔断,打印出错误资源文件路径:

Promise.all(
  parseFiles.map(p => p.catch(error => console.log('解析错误:', error)))
)

最后,手动处理下有问题的图片资源。

总结

目前我停留在“自己写脚本”这一步,用了 node.js 中一两个 api,也搭上了几个图片压缩的库。相比在线工具和直接 gulp,现在脚本的“机动性”好了不少,不再局限别人工具的束缚。

但真的就这样完了吗?

这里又要再提下一步:重构代码,达到像别人一样能给他们开箱即用的效果,这样自己才能算解决了“图片压缩”这个需求,甚至有必要,去研究下压缩的算法,或者试图写个 exe 之类的执行程序。

相信各位遇到过各种需求,需要解决各种难题,但真正“吃透”一个问题少之又少,而那些象牙塔的同学却在越走越高,我们的差距就是如此被拉大的,所以:好好学习,天天向上,与各位共勉。

转自https://www.toutiao.com/i6823001585723900420/?timestamp=1593354080&app=news_article&group_id=6823001585723900420&use_new_style=1&req_id=202006282221200100160322061541F1D2


评论关闭
IT序号网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!