图片压缩神器的批量使用,和简单拓展,tinyPNG + squoosh

发布于 2022-05-02  5098 次阅读


tinyPNG部分:

1,简单使用,

直接打开 tinyPNG官网 


点击加载功能选项,进入到图片选择的页面中,

在图片选择的页面中,找到需要缩小体积的图片,点击下方的打开选项,进入到下一步,

等待图片压缩完成后,点击下载功能选项,将图片下载保存至本地即可。

注意:这里一次性最多拖入20张图片,且单张不能超过5m,免费用户每天只能压缩20张。

 

 2,破解上传数量限制:

此处转载,点击此处阅读原文

前言:⽬前的 web 架构⼤多数都是通过 nginx 作为反向代理的,如下:

从上图可以看出,客户端并不是直接请求应用服务的,而是通过统一接入层(往往是 nginx) 来转发请求的。那么问题来,应用服务是如何获取到客户端的 IP 的呢?

玩过 `nginx` 的同学应该都知道 , 这个通用的解决方案就是 `X-Forwarded-For` 请求头。

简单的来说,就是所有的反向代理都实现一个统一的约定,在转发请求给下游服务之前,把请求代理的 IP 地址写入到 X-Forwarded-For 头中,形成了一个 IP 地址列:

X-Forwarded-For: client, proxy1, proxy2

这个方案虽然不是正式的 HTTP 协议,但已经成为了一个事实标准,基本上所有的反向代理服务都实现了这个功能,以确保下游的服务可以感知到经过的反向代理,并从中获取到用户的 IP 地址。

好了,既然应用服务是通过 `X-Forwarded-For` 头部来获取客户端 IP 的,那么我们能不能在客户端伪造这个头部,每次上传图片的时候都设置一个随机的 IP 呢?所以就决定尝试一下。结果发现,该方法确实可行!竟然可以欺骗 tinypng 的服务器,从而绕开了上传次数的限制。

完整代码如下:

const fs = require('fs');
const path = require('path');
const https = require('https');
const { URL } = require('url');


const cwd = process.cwd();

const root = cwd;
  exts = ['.jpg', '.png'],
  max = 5200000; // 5MB == 5242848.754299136


const options = {
  method: 'POST',
  hostname: 'TinyPNG – Compress PNG images while preserving transparency',
  path: '/web/shrink',
  headers: {
    rejectUnauthorized: false,
    'Postman-Token': Date.now(),
    'Cache-Control': 'no-cache',
    'Content-Type': 'application/x-www-form-urlencoded',
    'User-Agent':
      'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
  }
};

fileList(root);


// 生成随机IP, 赋值给 X-Forwarded-For
function getRandomIP() {
  return Array.from(Array(4)).map(() => parseInt(Math.random() * 255)).join('.')
}

// 获取文件列表
function fileList(folder) {
  fs.readdir(folder, (err, files) => {
    if (err) console.error(err);
    files.forEach(file => {
      fileFilter(path.join(folder, file));
    });
  });
}

// 过滤文件格式,返回所有jpg,png图片
function fileFilter(file) {
  fs.stat(file, (err, stats) => {
    if (err) return console.error(err);
    if (
      // 必须是文件,小于5MB,后缀 jpg||png
      stats.size <= max &&
      stats.isFile() &&
      exts.includes(path.extname(file))
    ) {
      // 通过 X-Forwarded-For 头部伪造客户端IP
      options.headers['X-Forwarded-For'] = getRandomIP();

      fileUpload(file); // console.log('可以压缩:' + file);
    }
    // if (stats.isDirectory()) fileList(file + '/');
  });
}

// 异步API,压缩图片
// {"error":"Bad request","message":"Request is invalid"}
// {"input": { "size": 887, "type": "image/png" },"output": { "size": 785, "type": "image/png", "width": 81, "height": 81, "ratio": 0.885, "url": "https://tinypng.com/web/output/7aztz90nq5p9545zch8gjzqg5ubdatd6" }}
function fileUpload(img) {
  var req = https.request(options, function(res) {
    res.on('data', buf => {
      let obj = JSON.parse(buf.toString());
      if (obj.error) {
        console.log(`[${img}]:压缩失败!报错:${obj.message}`);
      } else {
        fileUpdate(img, obj);
      }
    });
  });

  req.write(fs.readFileSync(img), 'binary');
  req.on('error', e => {
    console.error(e);
  });
  req.end();
}
// 该方法被循环调用,请求图片数据
function fileUpdate(imgpath, obj) {
  const outputDir = path.join(cwd , 'output');
  imgpath = path.join(cwd , 'output', imgpath.replace(cwd, ''));

  if(!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir);
  }

  let options = new URL(obj.output.url);
  let req = https.request(options, res => {
    let body = '';
    res.setEncoding('binary');
    res.on('data', function(data) {
      body += data;
    });

    res.on('end', function() {
      fs.writeFile(imgpath, body, 'binary', err => {
        if (err) return console.error(err);
        console.log(
          `[${imgpath}] \n 压缩成功,原始大小-${obj.input.size},压缩大小-${
            obj.output.size
          },优化比例-${obj.output.ratio}`
        );
      });
    });
  });
  req.on('error', e => {
    console.error(e);
  });
  req.end();
}

这段代码是参考了 [nodejs 全自动使用 Tinypng (免费版,无需任何配置)压缩图片](人类身份验证 - SegmentFault) ,只是简单的加上了动态的 IP 头,从而实现了不受上传次数限制。

核心的代码在这里

// 生成随机IP, 赋值给 X-Forwarded-For
function getRandomIP() {
  return Array.from(Array(4)).map(() => parseInt(Math.random() * 255)).join('.')
}

// ....
	options.headers['X-Forwarded-For']  = getRandomIP();
//....

GitHub地址

同时,也发布到了 npm 上面。只需要在nodejs环境下,使用全局安装命令行:

npm i super-tinypng -g

然后在你想要压缩图片的目录里面运行 `super-tinypng` 就能自动压缩图片了,并且不会有数量限制!

 

 

另外可以作为js脚本运行:

/** 
 * 帮助文档
 * -------
 * 
 * 获取帮助
 * 指令 -h
 * 
 * 获取命令执行文件夹 
 * 指令 -f 
 * 参数 ./
 * 必填,待处理的图片文件夹
 * 
 * 获取是否深度递归处理图片文件夹
 * 指令 -deep
 * 可选,默认不深度递归
 * 
 * 命令行脚本参考示例
 * > node ./脚本.js -f ./图片文件夹 -deep
 *  */

 const fs = require('fs');
 const path = require('path');
 const https = require('https');
 const URL = require('url').URL;
 const EventEmitter = require('events');
 const err = msg => new EventEmitter().emit('error', msg);
 
 if (getHelp()) return false;
 
 const conf = {
     files: [],
     EntryFolder: getEntryFolder(),
     DeepLoop: getDeepLoop(),
     Exts: ['.jpg', '.png'],
     Max: 5200000, // 5MB == 5242848.754299136
 }
 
 fileFilter(conf.EntryFolder)
 
 console.log("本次执行脚本的配置:", conf);
 console.log("等待处理文件的数量:", conf.files.length)
 
 conf.files.forEach(img => fileUpload(img));
 
  工具函数
 
 /**
  * 获取帮助命令
  * 指令 -h
  */
 function getHelp() {
     let i = process.argv.findIndex(i => i === "-h");
     if (i !== -1) {
       
         return true;
     }
 }
 
 /**
  * 获取命令执行文件夹 
  * 指令 -f 
  * 参数 ./
  * 必填,待处理的图片文件夹
  */
 function getEntryFolder() {
     let i = process.argv.findIndex(i => i === "-f");
     if (i === -1 || !process.argv[i + 1]) return err('获取命令执行文件夹:失败');
     return process.argv[i + 1];
 }
 
 /**
  * 获取是否深度递归处理图片文件夹
  * 指令 -deep
  * 可选,默认不深度递归
  */
 function getDeepLoop() {
     return process.argv.findIndex(i => i === "-deep") !== -1;
 }
 
 /**
  * 过滤待处理文件夹,得到待处理文件列表
  * @param {*} folder 待处理文件夹
  * @param {*} files 待处理文件列表
  */
 function fileFilter(folder) {
     // 读取文件夹
     fs.readdirSync(folder).forEach(file => {
         let fullFilePath = path.join(folder, file)
         // 读取文件信息
         let fileStat = fs.statSync(fullFilePath);
         // 过滤文件安全性/大小限制/后缀名
         if (fileStat.size <= conf.Max && fileStat.isFile() && conf.Exts.includes(path.extname(file))) conf.files.push(fullFilePath);
         // 是都要深度递归处理文件夹
         else if (conf.DeepLoop && fileStat.isDirectory()) fileFilter(fullFilePath);
     });
 }
 
 /**
  * TinyPng 远程压缩 HTTPS 请求的配置生成方法
  */
 
 function getAjaxOptions() {
     return {
         method: 'POST',
         hostname: 'tinypng.com',
         path: '/web/shrink',
         headers: {
             rejectUnauthorized: false,
             "X-Forwarded-For": Array(4).fill(1).map(() => parseInt(Math.random() * 254 + 1)).join('.'),
             'Postman-Token': Date.now(),
             'Cache-Control': 'no-cache',
             'Content-Type': 'application/x-www-form-urlencoded',
             'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
         }
     }
 }
 
 /**
  * TinyPng 远程压缩 HTTPS 请求
  * @param {string} img 待处理的文件
  * @success {
  *              "input": { "size": 887, "type": "image/png" },
  *              "output": { "size": 785, "type": "image/png", "width": 81, "height": 81, "ratio": 0.885, "url": "https://tinypng.com/web/output/7aztz90nq5p9545zch8gjzqg5ubdatd6" }
  *           }
  * @error  {"error": "Bad request", "message" : "Request is invalid"}
  */
 function fileUpload(imgPath) {
     let req = https.request(getAjaxOptions(), (res) => {
         res.on('data', buf => {
             let obj = JSON.parse(buf.toString());
             if (obj.error) console.log(`压缩失败!\n 当前文件:${imgPath} \n ${obj.message}`);
             else fileUpdate(imgPath, obj);
         });
     });
 
     req.write(fs.readFileSync(imgPath), 'binary');
     req.on('error', e => console.error(`请求错误! \n 当前文件:${imgPath} \n`, e));
     req.end();
 }
 
 // 该方法被循环调用,请求图片数据
 function fileUpdate(entryImgPath, obj) {
     let options = new URL(obj.output.url);
     let req = https.request(options, res => {
         let body = '';
         res.setEncoding('binary');
         res.on('data', (data) => body += data);
         res.on('end', () => {
             fs.writeFile(entryImgPath, body, 'binary', err => {
                 if (err) return console.error(err);
                 let log = "压缩成功"
                 log += `优化比例: ${ (( 1 - obj.output.ratio) * 100).toFixed(2) }%`
                 log += `原始大小: ${ (obj.input.size / 1024).toFixed(2) }KB`
                 log += `压缩大小: ${ (obj.output.size / 1024).toFixed(2) }KB`
                 log += `文件:${entryImgPath}`
                 console.log(log);
             });
         });
     });
     req.on('error', e => console.error(e));
     req.end();
 }
 
 // node ./tinypng.js -f ./static/smp/m/course_task

复制上面的代码,直接在node环境下运行命令: node 脚本.js -f 图片文件夹路径即可。

 

 

3,破解上传大小限制:

其实并非破解,是官方给第三方开发者的使用方法

点击官网右上角注册账号,

注册之后,点击THIRD-PARTY,找到tinyPNG.App  选择相应系统下载

点击DEVELOPER API

输入自己的账号,邮箱,即可获取相应的key,然后复制key值

 

粘贴到你下载好的软件里

 

如上,就成功了,上传就不限制大小,拖入图片后,等待后他会在源文件目录下自动生成压缩图片, 注意的是,每个api每个月只有500张免费数量,每个邮箱账号都会生成一个key,500张,如果图片量需求不是很大的话,已经绰绰有余了。

拓展部分:TingPNG拓展性很强,无论是APi的利用性,还是其他,集成在PS的插件,WordPress上插件等等。

 

 

squoosh部分:

1,Squoosh 网页版

 

  打开squoosh官网

 

 

页面非常简洁,上面是一个可以打开图片的按钮,下面是几个示例图片。我们打开其中的一个示例图片,会弹出以下编辑界面:

 

 

界面将图片使用一个可移动的分界线分成两个部分,可以用于对比压缩前后的两张图片,也可以对比两种不同压缩方式的图片。两张图片各有一个参数选择框,分别在左下角和右下角。在 Compress 一栏可以选择压缩的方式。左面默认为 “Original image”,即不进行压缩。右面默认为 MozJPEG ,是一种将图片压缩为 JPEG 格式的方法。选择了方法后,还可以选择对应的参数。比如对于 JPEG 压缩,可以选择它的质量,这里选择 75 ,可以看到,图片的尺寸从原来的 2.79 MB 下降到 862 kB ,下降了69%。而拖动分界框,可以发现,图片的质量降低很小,基本可以保持清晰。

此外,在 Edit 菜单中,还提供了其它编辑选项。使用 MozJPEG 时,有重设图片大小 Resize 和减少颜色数量 Reduce palette 两个选项,可以进一步地压缩图片,读者可以自行探索。

对生成的图片满意后,可以点击下面的 下载 按钮,将图片直接保存至本地。整个操作都是在浏览器中完成的,使用的是 WebAssembly 技术,非常方便使用,在各种操作系统中都可以使用,而且有浏览器就可以,不需要安装任何其它软件。

2,Squoosh 命令版

如果需要对大量图片进行批量压缩,可以使用 Squoosh 提供的命令行工具。目前它是基于 Node.js 的。如果你的电脑中安装了 Node.js ,可以很方便地使用。其语法为

注意Linux和window的区别,建议阅读官方GitHub文档

 

安装Squoosh CLI:

npm i -g @squoosh/cli

代码非常简洁。对于新手来说,可以从浏览器中直接生成代码。在刚才的浏览器页面中,在右下角 Edit 对话框右侧有两个图标,其中第一个表示命令行的图标,点击后就可以生成命令行代码,并复制到剪贴板。生成的代码为:

npx @squoosh/cli --mozjpeg '{"quality":80,"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}'

复制结果可先粘贴到txt中

注意:这里有个坑,复制出来的参数直接用会报错,需要把 ' 和 " 给全部去掉才可以。

npx @squoosh/cli --mozjpeg {quality:80,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}

编写循环及输出路径
在上面复制出的基础上进行添加循环和设置输出路径:
*这里我选择要压缩图片的原格式是“.JPG”,输出路径是“E:\桌面\compress\”

for %a in (*.JPG) do squoosh-cli --mozjpeg {quality:80,baseline:false,arithmetic:false,progressive:true,optimize_co ding: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} %a -d E:\桌面\compress\

打开cmd,切换到你要压缩的图片文件夹下

粘贴上面完整代码,回车运行即可。