Sharp: 压缩图片用啥 TinyPNG, 我也可以, 而且还免费呢?
引言
图片压缩在项目中是一个常见的需求, 通过减小图片文件的大小, 降低存储和传输成本, 提高网页加载速度, 以及减少带宽占用!
一、现有方案「TinyPNG」介绍
正如引言所述, 目前个人站点压缩图片用的是 TinyPNG 所以在开始前, 容我简单介绍下它!!!
1.1 是什么?
正如官网所说, TinyPNG
是一个强大的图片压缩工具, 它使用智能有损压缩技术可以将我们的的 WebP
、PNG
、JPEG
等图片的文件大小进行一个压缩, 它可以通过选择性的减少图片中的颜色, 使得只需要很少的字节数就能保存数据, 在视觉上压缩的图片前后影响几乎不可见, 但是在文件大小上却有着显著效果!!!
同时 TinyPNG 官方客户端代码库支持各种语言, 通过官方提供的 SDK
我们可以轻松调用它们的 API
, 实现图片压缩功能!!!
当然天下没有免费的午餐, TinyPNG API 每月是会有 500次
的免费额度, 超过部分则需要收费咯!!! 当然个人使用的话 500次
基本就够用了!!
1.2 怎么用?
WEB
版: 直接进入 TinyPNG 首页, 如图, 将图片拖拽到指定区域或者直接点击(会弹出文件选择窗)选择图片, 选择完图片将自动压缩
压缩完成会有下载入口, 直接下载即可
Node
环境使用: 首先需要申请一个API
密钥, 这里需要先登录(填写邮件, 邮箱会有个登录按钮, 点击就可以登录); 登录后进入「开发者 API」页面, 然后填写信息获取API
密钥
这里也会发一个邮箱, 点击邮箱邮件中按钮会来到 API
管理页面, 这里我们就可以看到一个 API
密钥了
有了 API
密钥, 在 Node
中我们就可以使用 tinify 来完成图片的压缩了
import tinify from 'tinify'
// 1. 设置 api key
tinify.key = "YOUR_API_KEY";
// 2. 方法一: 输入为 buffer, 输出为 buffer
tinify.fromBuffer(buffer).toBuffer((err, resultData) => {
error = err;
resolve(err ? buffer : resultData);
});
// 3. 方法一: 输入为「图片路径」, 输出为「图片路径」
tinify.fromFile("unoptimized.png").toFile("optimized.png");
1.3 为什么要放弃?
- 每个月只有
500次
的免费额度, 虽然目前够用, 但未来就不一定咯(毕竟砸们也是有很大愿景的) - 无法压缩
GIF
- 既然
Node
可以自己做, 又何必受限于 TinyPNG
二、新方案「Sharp」
新方案就是使用 Sharp 来实现, 有了它我们就可以在 Node
中实现图片的压缩, 可以将常见格式的大图转换为较小的、网络友好的不同尺寸的 JPEG
、PNG
、WebP
、GIF
和 AVIF
图像, 同时除了压缩图片, 它还提供了很多强大的图片处理能力....
Sharp 使用起来也炒鸡简单, 如下所示, 只需要一行代码即可完成图片的压缩, 其中 quality
是压缩图片常见的一个参数, 它指的是图片压缩后的 质量
, 该值可选范围为 1 ~ 100
, 数值越高图片质量越好相应的压缩比例也就越低, 该值通常建议为 75-80
, 不建议小于 60
import sharp from 'sharp'
sharp('1.png').png({ quality: 75 }).toFile('2.png')
如果安装
sharp
失败, 请参考第四节「Sharp
安装失败解决办法」部分内容
来测试下效果: 随便找了个图, 测试了下, 直接将从 112KB
压缩到了 31KB
, 这压缩率还是很给力的!!!!
特别的是 gif
、raw
、tile
是不能调整 quality
参数的, 所以如果想要对 GIF
进行压缩就得另外处理了, 如下代码所示:
animated
: 设置为true
可读取动画图像(GIF、WebP、TIFF)的所有帧, 否则默认只会读取第一帧limitInputPixels
: 默认情况下 Sharp 能处理的像素数是有限制的(268402689
), 所以图片如果太大将会报错, 如果要解除该限制就可以将该参数设置为false
colours
: 设置调色板(基础颜色)的最大数量, 它的值介于2
到256
之间, 数值越小那么绘制出来的gif
画质越差, 但是体积相对也越小!! 所以这里得自己多做些尝试, 找到一个合适的数值!!
sharp('ScreenFlow.gif', {
animated: true,
limitInputPixels: false
}).gif({
colours: 2,
}).toFile('2.image.gif')
如下图, 是在 colours
设置为 2
情况下的对比图, 可以发现画质严重损坏了, 但是文件体积直接从 36.3MB
-> 2.1MB
这里我们还是那句话需要耐心得多做些测试, 找到一个合适的数值, 下面是 128
的一个情况, 只要不放大看还算能接受, 体积也是从 36.3MB
-> 3.3MB
三、项目改造
- 先安装
sharp
依赖
npm i sharp
如果安装
sharp
失败, 请参考第四节「Sharp
安装失败解决办法」部分内容
- 下面我们使用
sharp
来封装一个通用的方法, 由于gif
、raw
、tile
是不能调整quality
参数, 并且针对gif
我们还需要特殊处理, 所以就需要对图片进行判断处理, 具体实现如下:
- 函数的入参和出参都是文件流(
stream
)数据, 所以这里针对stream
和buffer
做了些转换工作 switch
根据不同文件格式
设置不同的options
、formatOptions
await sharp(imagesBuffer).metadata()
使用sharp
获取文件的meta
数据
import sharp from 'sharp';
import { streamToBuffer, bufferToStream } from '#utils/fs';
/**
* 压缩图片
*
* @param {stream} stream 要压缩图片流
* @returns {stream} 返回的是压缩后的流
*/
export default async (stream) => {
const imagesBuffer = await streamToBuffer(stream);
const metadata = await sharp(imagesBuffer).metadata();
let options = {}; // sharp 配置
let formatOptions = {}; // 不同格式方法参数
// 根据文件格式, 设置不同的配置
switch (metadata.format) {
case 'gif':
options = {
animated: true,
limitInputPixels: false,
};
formatOptions = { colours: 128 };
break;
case 'raw':
case 'tile':
break;
default:
formatOptions = { quality: 75 };
}
// 压缩: 调用 sharp
const newBuffer = await sharp(imagesBuffer, options)
?.[metadata.format](formatOptions)
.toBuffer();
// 返回文件流
return bufferToStream(newBuffer);
};
最后补充两个 stream
和 buffer
相互转换的方法
// #utils/fs
// Stream 转 Buffer
export const streamToBuffer = (stream) => new Promise((resolve, reject) => {
const buffers = [];
stream.on('error', reject);
stream.on('data', (data) => buffers.push(data));
stream.on('end', () => resolve(Buffer.concat(buffers)));
});
// Buffer 转 Stream
export const bufferToStream = (buffer) => {
const stream = new Duplex();
stream.push(buffer);
stream.push(null);
return stream;
};
- 压缩效果: 最后看下
png
和gif
的一个压缩效果吧, 压缩率还是很给力, 图片质量还是ok
的
- 最后卸载
tinify
, 清理相关代码
npm uninstall tinify
四、Sharp 安装失败解决办法
Sharp
是基于 libvips
实现的, 所以在安装 sharp
时会根据系统自动请求下载对应的 libvips
, 但这个 libvips
国内用户很难下载成功!!!
目前解决办法就是要么自备好用的梯子🪜, 要么就是按照 官方文档 单独设置 sharp
、libvips
的 npm
镜像地址, 然后在进行下载
npm config set sharp_binary_host "https://npmmirror.com/mirrors/sharp"
npm config set sharp_libvips_binary_host "https://npmmirror.com/mirrors/sharp-libvips"
npm install sharp
五、参考
如果你对我的文章感兴趣, 欢迎关注我的个人公众号 「昆仑虚 F2E」, 让我们一起探索前端的奇妙世界!
转载自:https://juejin.cn/post/7291598231954948152