写在前面
众所周知,前端有许多切图的需求,其中对于图片的优化与前端也息息相关。因此无论是前端工作,还是博客的运营,亦或是希望可以用更小的成本玩转图片,必定涉及大量图片操作需求。与图片打过一些交道了,自然而然就会产生一些心得。
而在这其中,图片压缩是优化的重要手段。LCP 同样作为用户感知明显的指标之一,而图片的加载直接影响 LCP。在众多的前端面试题中,我们会经常看到图片优化的手段,但是一些优化手段如懒加载,其实也会影响用户的体验。而图片压缩,只要使用无损压缩,那么用户的体验会更好,是典型的无副作用方案。本文将介绍我平时用的顺手,并且效率也相对较高的图片处理模式。
本文仍然在施工中…只是个半成品,后续如果有更好用的方法,我会及时更新。
图片格式转换
WebP 是一种现代图片格式,可为网络上的图片提供出色的无损和有损压缩。使用 WebP,网站站长和 Web 开发者可以创建更小、更丰富的图片,从而提高网页加载速度。
与 PNG 相比,WebP 无损图片的尺寸缩小了 26%。WebP 有损图片比采用等效 SSIM 质量指数的同等 JPEG 图片小 25-34%。
无损 WebP 支持透明度(也称为 Alpha 通道),但只需额外增加 22% 的字节。对于可以接受有损 RGB 压缩的情况,有损 WebP 也支持透明度,其文件大小通常比 PNG 小 3 倍。
动画 WebP 图片支持有损、无损和透明度,与 GIF 和 APNG 相比,此类图片可缩减大小。
如今都 2024 了,我们没有理由继续使用不再支持 WebP
格式的设备。事实上不支持的设备凤毛麟角。所以放心大胆的将图片转换为 WebP
吧!除了 Python 提供类似于 image 这样的库之外,还有一些工具可以帮助我们完成图片格式的转换。
以下为 Python 提供的能力,可谓是门槛最低的方法了:
- 使用 cv2 库将图片从 png 格式转换为 webp 格式
import cv2
# 读入 PNG 格式的图片
img = cv2.imread('input.png')
# 转换为 WebP 格式的图片,并保存
cv2.imwrite('output.webp', img, [int(cv2.IMWRITE_JPEG_QUALITY), 100])
- 使用 PIL 库将图片从 webp 格式转换为 png 格式
from PIL import Image
img = Image.open('input.webp')
img.load()
img.save('output.png')
集成到系统原生
https://developers.google.com/speed/webp/docs/precompiled?hl=zh-cn
考虑到 WebP 是由 Google 发起的,我们可以方便的使用 Google Developer 提供的建议。我们甚至可以将 WebP 格式转换的能力内置进入系统,像 node 一样使用。如果我们这么做了,可能第一次操作的时候需要摸索,但是一旦习惯了,以后我们可以方便的使用命令行文件进行处理。
笔者使用的是 MacOS 系统,因此会比较详细的列举 Mac 的使用方法。或许我们日后可以使用类似的方法在 Linux 上使用它。Windows 系统的话,先咕了,去官网应该能看懂怎么下载。
- 使用 homebrew 安装 webp。
brew install webp
. 请先确保你的 brew 可用,然后再进行安装。
homebrew 链接:https://formulae.brew.sh/formula/webp
以上过程会将整个 webp 的转换工具都安装到你的系统中,如果你的系统已经安装了 webp,那么可以使用 brew info webp
查看实际上也就是把 homebrew 的官网文字给你输出了一遍而已。
它包括了:cwebp
dwebp
(将图片转换为原来的格式) img2webp
等工具。这里我们先使用 cwebp
举例子,进行转换。
- 压缩为 WebP 无损压缩,需要保证 input.png 在当前目录下。
我们使用 cwebp
工具中的“无损”选项将 PNG 转换为 WebP 无损格式。要获得最少的输出,所使用的确切命令行如下所示:
cwebp input.png -lossless -m 6 -q 100 -o webp_lossless.webp
- 压缩为 WebP 有损压缩(使用 alpha 通道)
我们使用 cwebp
工具将 PNG 转换为 WebP 有损(带有 alpha 通道)。我们选择的 WebP 质量为 90(有损压缩),alpha 质量为 100(无损压缩)。使用的确切命令行如下所示:
cwebp input.png -q 90 -alpha_q 100 -m 6 -o webp_alpha.webp
关于 cwebp
的文档,可以查看:https://developers.google.com/speed/webp/docs/cwebp?hl=zh-cn
有了上述技能,我们完全可以写一个脚本,批量将图片转换为 webp 格式。例如,如需转换文件夹中的所有 jpeg 文件,请尝试执行以下操作:
for F in *.jpg; do cwebp $F -o `basename ${F%.jpg}`.webp; done
如果是无损压缩,我们只需要使用 -lossless
参数即可,当然,为确保最大的压缩比率,这里也可以使用 -q
。
for F in *.jpg; do cwebp "$F" -lossless -q 100 -o "`basename ${F%.jpg}`.webp"; done
for F in *.png; do cwebp "$F" -lossless -q 100 -o "`basename ${F%.png}`.webp"; done
当然,用这两种方法不会删除原有的图片,如果我们需要删除原有的图片,可以使用 rm
命令。
rm -rf *.png
rm -rf *.jpg
或者集成:
for F in *.jpg; do cwebp "$F" -lossless -q 100 -o "`basename ${F%.jpg}`.webp" && rm -rf "$F"; done
for F in *.png; do cwebp "$F" -lossless -q 100 -o "`basename ${F%.png}`.webp" && rm -rf "$F"; done
当然,这种方法的另一个好处是,我们完全可以从开源的源代码自行编译成我们需要的程序,可以参考这个链接,这里不再做说明:https://developers.google.com/speed/webp/docs/compiling?hl=zh-cn
修图工具
PhotoShop
如果 2024 年了,仍然有修图工具不支持导出 WebP
格式,那么你也完全没必要使用它了。
这里以 PhotoShop 为例子,大致说下如何转换:
- 打开图像处理软件,如 PhotoShop、GIMP 等。
- 导入要转换的图片文件。
- 在软件中打开“文件”菜单,选择“另存为”。
- 在弹出的“另存为”对话框中,选择“
WebP
”格式作为输出格式。
这里也有一位作者分享了他批量转换格式的姿势:https://blog.zhheo.com/p/1d8e31d8.html#%E5%A4%A7%E5%8A%9F%E5%91%8A%E6%88%90
不过,如果我们只是打开 PS 然后改下图片的格式,未免太大材小用。尤其是部分性能不好的电脑,PS 启动关闭可能都需要 1 分钟以上,我开个 PS 就为了改个图片格式?更不用说很多程序员的电脑里面都没有携带 PS 这款软件。自然而然,我们会倾向于使用更轻量化的工具。
ImageManager
ImageManager 是一款 VS Code 插件。相比起 PhotoShop,它具备两个优点:可以在 VS Code 内「自给自足」,并且足够轻量化。当然,它之所以能轻量化的原因是因为它明白它主要的功能都是给程序员提供的。因此,它提供的所有功能都是程序员用的比较多的,对于研发来说是比较实用的。
顺便也膜拜下原作者,这是他在稀土掘金发布的本文链接:https://juejin.cn/post/7348004403016794147
作者对于自己的工具,显得十分自信:
同一张图片,TinyPNG 压缩结果为 136kb,时间大概 5s;而 image-manager 压缩效果为 92kb,不到 1s!
如果把上传图片、下载图片这些繁琐的操作加到一起,或许 vscode 插件的效率要高上 10 倍有余
这个插件的主要使用方法,作者仍然定义为了「图片压缩」,所以才会去和后文提到的 TinyPNG 进行对比。不过,这个插件也可以实现批量格式转换。如图所示,Image Manager 中,我们选择了一个文件夹下的图片,然后点击「转换」。转换成 webp 后,确实减少了 68% 的体积,同时也会对图片进行压缩。
在使用的时候,MacOS 使用 Shift+Command+P
快捷键,打开命令面板,输入 Image Manager
即可进入这个页面。
不过就笔者的使用体验而言,原生转换为 webp 格式后,使用 TinyPNG 压缩的效果是比这个插件要好的,所以感觉也不能直接把结论下了,应该是和图片本身存在一定的联系,部分图片使用 TinyPNG 效果好,部分使用插件效果好。不过就易用性而言,这个插件确实是做到了极致,而且转换的速度确实也非常快。
在线转换
网站上提供在线转换的服务商一大把,这里需要选取几个放在这里展示也是很不容易的。需要说明的是我的选取标准,首先就是质量。至少转换的质量非常高,转换后需要用肉眼看不出来。在某些能选择转换质量的网站上,把质量选到最高,需要能满足这一条件。其次是必须要免费,咱不能为了这唾手可得的功能花钱啊。当然,在线服务的网络状况也是一个重要因素,如果说图片上传下载的体验不好,那么实际上再好的压缩算法,易用性也很差。其中也有部分服务在国内体验可能不太好,因为众首周知的原因。当然,这个部分具体取决于当前使用者的网络状态,我会尽力说明。
To WebP
ToWebp.io 是一款免费在线工具,可立即转换 WebP,无需将文件上传为标准 JPG、JPEG、PNG、AVIF、GIF 和 ICO。同时,将所有可能的图像格式转换为WebP图像格式。
这个网站应该是最纯净的将图片转换为 WebP 的网站,并且完全免费。
如图所示,它支持拖拽上传,并且支持批量转换。并且具有为数不多的可以调整一些参数的功能。例如不想要太高的转换质量,那么可以调整一下,这个在极致优化的时候感觉会很有用。除此之外,他可以将图片进行缩放,这一下子就将图片压缩的「三板斧」都集齐了。
可惜的是,它不支持 API 调用。所以仅仅适合于顺手使用下的场景。
Pixelied
Pixelied 是一家提供相片编辑器的服务商,旗下有个「Free Image Converter」的图片格式转换工具,让使用者通过浏览器进行在线批次编辑,而且不用担心降低照片画质,也无需下载或安装任何应用程序,提供最快速、免费且易于使用的图片转文件功能,这项服务支持各种常见图片格式包括 PNG、JPG、WebP、SVG、GIF、AVIF 和 TIFF 等超过 100 种格式。
https://pixelied.com/convert/jpg-converter/jpg-to-webp
它最大的优点是:免费且无限制的转换。(后文介绍的一些在线转换服务实际上有转换限制),这让我们在转换的时候可以无负担的使用。
他们家主要靠图片编辑收钱,因此提供了一些类似于云的服务。好在免费用户也能享受到部分存储空间。据介绍,免费用户有 2GB 的存储空间,确实比较良心。因此完全可以把他家进一步使用当成简易的图片编辑器来使用。付费版的区别主要是能使用更多素材,更多空间和一些 AI 能力,这些对于写代码来说反而不是很需要。
不过他家似乎没有 API 调用,因此我们无法通过代码来实现批量转换。
Convertio
Convertio 是一个线上文件转换工具,它允许用户将文件从一种格式转换为另一种格式。它支持大多数常见的档案格式,包括图像、文件、音讯、影片、电子表格和更多。Convertio 还提供一些额外的功能,例如将 PDF 转换为可编辑的档案格式,或者将多个文件合并为一个单一的文件。
https://convertio.co/zh/jpg-WebP/
进去之后,我想它友好的页面设计会让您知道怎么操作的,这里就不详细展开了。
当然,它仍然有一些缺点,并且可能在部分人看来实际上没办法接受。首先是国内访问速度可能较慢,并且在特定条件下,下载的速度极慢。其次,它的免费计划限制较多,文件最大只能 100MB,在 24h 内只能转换 10 个文件,并且最多并行 2 个文件。
它支持 API 调用的方式,这块单独写在后面。
CloudConvert
CloudConvert 最引以为豪的就是它支持 218 种文件格式的转换,当然也可以随意转换图片格式。在它的网站上,你能看到它「Convert Anything to Anything」的标语。事实上,CloudConvert 不但支持转换同类别的文件格式(比如将 JPG 转换为 PNG),它甚至可以跨类别转换,比如将 DOCX 文档转换成 PNG。
不过它的免费额度也有限制,每天最多转换 25 个文件,和上面的那家如出一辙。
它同样支持 API 调用方式。
改图宝
改图宝是我比较用的顺手的在线工具,主要是它特别轻量化,并且也会对图片进行一些损害不高的压缩操作。至少我个人用着没啥问题,也没有遇到过收费的情况。
https://www.gaitubao.com/jpg-gif-png
它的页面也十分友好,进去之后很容易找到图片格式转换的地方。
比 WebP 更优秀的格式
当然,比 WebP
格式更好的图片,例如 avif
,heic
等格式,目前还不支持多数浏览器的解析,贸然使用可能会导致图片无法正常显示。这种时候我们需要使用图片降级渲染策略。
<picture>
<source srcset="img/photo.avif" type="image/avif"> // 如果浏览器支持使用
<source srcset="img/photo.webp" type="image/webp">
<img src="img/photo.jpg" alt="Description of Photo"> // 浏览器不支持
</picture>
当然,我们也可以使用 JS 方法来进行判断:
// npm install avif.js
// 下面代码放到reg.js中,然后把avif-sw.js放在web服务器根目录
require("avif.js").register("/avif-sw.js");
<body>
<!-- 注册worker -->
<script src="reg.js"></script>
<!-- 使用IMG标签嵌入AVIF图像 -->
<img src="image.avif">
<!-- 或者通过CSS属性 -->
<div style="background: url(image2.avif)"></div>
</body>
如上图,我们甚至可以依赖 service worker 的请求拦截特性,当页面发出 fetch
操作时,它可以将请求拦截住,然后给出自己的响应,这样就能在请求完 avif
格式的图片以后,在 service worker 当中调用解码器,将图片转码。同时,因为是运行在 service worker 线程当中,解码操作并不会阻塞 UI 线程。
另外,由于 avif
是基于 AV1 视频编码的,Chrome 等浏览器在很早之前的版本就内置了 AV1 的解码器,但是直到最近才支持 avif
的解析。
所以实际上在内置了原生解码器的浏览器版本里面,我们就不需要使用这个 polyfill 实现的 JS 版本解码器了,可以直接使用效率更高的原生解码器。
当你还在犹豫不决时,bilibili已经在他们的网站上使用了上了 avif 格式~ 你可以点进去它们的一篇专栏,然后用最新版本的 Chrome 去查看,看看是不是已经使用了 avif
格式的图片了。
使用 API 进行转换
Convertio
https://developers.convertio.co/zh/
首先需要注册获得一个 API_KEY。这个过程是免费的。
这是 API 文档:https://developers.convertio.co/zh/api/docs/,官方给出了一些参数:
Field | Type | Description | |
apikey | String | Your API Key | |
input | String | Method of providing the input file. Default Value:url Allowed Values: url,raw,base64,upload | |
file | String | URL of the input file (if input=url), or file content (if input = raw/base64) | |
filename | optional | String | Input filename including extension (file.ext). Required if input = raw/base64 |
outputformat | String | Output format, to which the file should be converted to. | |
options | optional | Object | Conversion options. Now used to define callback URL, enable OCR and setting up its options. You may find available OCR conversion options here and callback example here. |
不过也像上面所说,它拥有一个免费计划,因此 API 的调用并不是无限的,好在它的限制措施也不多,只有一条:每 24h 转换 25 个文件,其它的限制也没有了,不会像有些产品,指定一堆复杂的政策骗钱。不过具体的我还没试用过,所以暂时没有 demo,平时一般都是线上服务解决问题也够了。
另外,这家的 API 服务似乎没有一些常用语言的 SDK,这可能会对开发者带来一些麻烦。(只有 PHP 的,所以 PHP 是不是世界上最好的语言呢 233)
不得不说,换个账号大法确实好用,希望这些网站的管理者没有看到我说的这句话。
CloudConvert
https://cloudconvert.com/pricing
它也同样需要注册来免费获取一个 key。相比于 Convertio,它的好处是支持了各种语言的 SDK,在开发的时候更加得心应手。不过他的免费额度也只有每天 25 个文件。
以 node 为例子,它的 SDK 在 Github 给出了比较详细的 demo:https://github.com/cloudconvert/cloudconvert-node
它的调用相比于其他的 API 来说,心智相对复杂一些。这可能是为了兼容多种文件的转换格式,因此它提供了 job
的概念。比如说下图就是创建了一种「工作」,其中包括了上传,转换,导出 3 个步骤。
// npm install cloudconvert --save
const CloudConvert = require('cloudconvert');
const cloudConvert = new CloudConvert('api_key');
const importUrl = path.join(__dirname, 'my-file.jpg');
const outputName = 'output-file';
let job = await cloudConvert.jobs.create({
tasks: {
'import-my-file': {
operation: 'import/url',
url: importUrl
},
'convert-my-file': {
operation: 'convert',
input: 'import-my-file',
output_format: 'webp',
some_other_option: 'value'
},
'export-my-file': {
operation: 'export/url',
input: outputName
}
}
});
CloudConvert 给每个使用了 export/url
的任务都会返回一个公共的,能在互联网访问的 url,我们就可以通过这个 url 来获取到转换后的文件。
job = await cloudConvert.jobs.wait(job.id); // Wait for job completion
const file = this.cloudConvert.jobs.getExportUrls(job)[0];
const writeStream = fs.createWriteStream('./out/' + file.filename);
https.get(file.url, function (response) {
response.pipe(writeStream);
});
await new Promise((resolve, reject) => {
writeStream.on('finish', resolve);
writeStream.on('error', reject);
});
这个 SDK 还提供了沙盒环境,使得用户在测试 API 的时候不至于使用掉免费额度。
// Pass `true` to the constructor
const cloudConvert = new CloudConvert('api_key', true);
总体使用下来,API 还是最推荐这款,虽然说免费额度一致,相比于 Convertio 使用友好,也可以避免在测试的时候丢失配额。速度也总体上比 Convertio 来的更快。
图片压缩
除了格式转换之外,我们还需要考虑图片压缩的问题。一般来说,图片压缩在绝大多数情况下可以取得非常好的效果。毕竟一般图片的生产方没有压缩的意识。
如果说格式的转换可能有比较固定的范式,那么图片压缩可以说是十分考验压缩算法的水平了。不同的压缩算法,在压缩率、压缩速度、压缩质量上都有着不同的表现。市面上能提供十分有竞争力的压缩能力的厂商属实不多,愿意免费的更是少之又少。除开压缩算法,如果我们正好有让图片大小变小的需求,那么图片的大小优化也肯定会很可观。比如说网站的大图没有必要使用 8k 的大图做展示,4k 就够了。如果不是刻意的保存需求,那么我们往往能创造一个改变图片大小的环境。不过图片大小也是一定会损害图片质量的。
在这个过程中,我们自然是希望图片的质量不能损失太多。毕竟很多图片一旦失去了高清晰度,就会让显示效果大打折扣。
系统自带
MacOS 系统自带图片的压缩功能。Apple 的官方说明中提及了这一点:
https://support.apple.com/zh-cn/guide/preview/prvw2015/mac
- 在 Mac 上的“预览” App 中,打开想要更改的文件。
- 选取“工具” > “调整大小”,然后选择“重新采样图像”。
- 在“分辨率”栏中输入一个较小的值。
- 新的大小显示在底部。
【提示】若要同时减小多个图像的文件大小,请在同一个窗口中显示这些图像,并在窗口的边栏中选择它们,然后选取“工具” > “调整大小”。
首先 Apple 的能力确实还是值得信赖的,压缩图片很快。并且在这个过程中,可以选择一些常见的图片大小,系统能力集成,也让这个过程执行的非常丝滑方便。但是缺点也很明显:那就是能选择的尺寸太少了,且最高只支持到 1280*720
. 在当前场景下,它确实用处只会越来越小。
在这个过程中,甚至还能顺带转换下图片的格式,可惜不支持 WebP
。如果转换为 png
格式,有的时候甚至图片还会变大…
imagemin
作为前端开发,imagemin 自然是我们再熟悉不过的产品之一了。它的最大优势是我们可以集成在 CI/CD 环境中。每当我们进行打包的时候,我们可以在打包的过程中直接使用 imagemin 来进行图片压缩。
Imagemin 是围绕「插件」构建的。插件是用于压缩特定图片格式的 npm 软件包(例如,mozjpeg
会压缩 JPEG)。选择插件时,最重要的考虑因素是它是「有损」还是「无损」。在无损压缩中,不会丢失任何数据。有损压缩会减小文件大小,但代价是可能会降低图片质量。如果插件未提及它是「有损」还是「无损」,您可以通过其 API 来判断:如果您可以指定输出的图片质量,则它是「有损」的。
图片格式 | 有损插件 | 无损插件 |
---|---|---|
JPEG | imagemin-mozjpeg | imagemin-jpegtran |
PNG | imagemin-pngquant | imagemin-optipng |
GIF | imagemin-giflossy | imagemin-gifsicle |
SVG | imagemin-svgo | |
WebP | imagemin-webp | imagemin-webp |
在通过跟打包工具集成 imagemin 之后,我们就可以在打包的过程中,使用 imagemin 来进行图片压缩。这里使用 Webpack 为例子:
const ImageminPlugin = require('imagemin-webpack-plugin').default;
const CopyWebpackPlugin = require('copy-webpack-plugin');
const path = require('path');
module.exports = {
entry: './index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CopyWebpackPlugin([{
from: 'img/**/**',
to: path.resolve(__dirname, 'dist')
}]),
new ImageminPlugin({
pngquant: ({ quality: [0.5, 0.5] }),
})
]
}
此代码会告知 Imagemin 使用 Pngquant 插件压缩 PNG。quality 字段使用 min 和 max 值范围来确定压缩级别 - 0 是最低压缩级别,1 是最高压缩级别。如需强制所有图片以 50% 质量压缩,请将 0.5 同时作为最小值和最大值传递。
假设我们还需要压缩 jpg 文件,那么我们需要添加如下配置:
const imageminMozjpeg = require('imagemin-mozjpeg');
new ImageminPlugin({
pngquant: ({ quality: [0.5, 0.5] }), // 已有配置,无需新增
plugins: [imageminMozjpeg({ quality: 50 })]
})
可以说,每种插件的具体用法都不一样,具体得您自己去查阅对应插件的文档。然后我们根据项目的需要,按需进行配置。比如你的项目如果确实只是用了 png
和 jpg
两种图片格式,那么你只需要配置 pngquant
和 mozjpeg
即可。
我们甚至还可以将 Imagemin 本身用作 Node 脚本。以下代码使用 imagemin-mozjpeg
插件将 JPEG 文件的质量压缩为 50(0 表示最差;100 表示最佳):
const imagemin = require('imagemin');
const imageminMozjpeg = require('imagemin-mozjpeg');
(async() => {
const files = await imagemin(
['source_dir/*.jpg', 'another_dir/*.jpg'],
{
destination: 'destination_dir',
plugins: [imageminMozjpeg({quality: 50})]
}
);
console.log(files);
TinyPNG
在官网的介绍中,他们这么写:TinyPNG 使用智能有损压缩技术将您的 WebP
, PNG
和 JPEG
图片的文件大小降低。 通过选择性的减少图片中的颜色,只需要很少的字节数就能保存数据。 对视觉的影响几乎不可见,但是在文件大小上有非常大的差别。
经过实测,无论是什么环境下,他们的图片损失都很小,但是压缩率非常高。
https://tinypng.com/ 进入之后直接上传即可,每次最多上传 20 张图片。一般来说足够支持需要顺手支持的场景。
黑魔法
偶然在知乎上看到了无限制使用 node 压缩图片的解法,先上 Github 地址:(这个并不是我写的,特此注明下)
知乎原文链接:https://zhuanlan.zhihu.com/p/152317953
这里我概括下作者的思路(大部分为作者原文,这里稍微提炼了下):
网页版的服务,我们仍然可以达到「无限使用」的效果——事实上我们完全不需要在云端保存那 20 张图片,这在不登录的情况下,是一定存在漏洞的。因此我们可以直接尝试使用网页和服务器的接口实现图片的压缩。
一般来说,这种免登录就可试用的系统,都是通过用户 IP 来限制用户的操作次数的。目前的 web 架构大多数都是通过nginx
作为反向代理的,换句话说,客户端并不是直接请求应用服务的,而是通过统一接入层(往往是nginx
) 来转发请求的。而nginx
恰恰约定了X-Forwarded-For
请求头告知下游服务用户的 IP。实际上,基本上所有的反向代理服务都实现了这个约定,以确保下游的服务可以感知到经过的反向代理,并从中获取到用户的 IP 地址。
那么我们能不能在客户端伪造这个头部,每次上传图片的时候都设置一个随机的 IP 呢?所以就决定尝试一下。结果发现,该方法确实可行!
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'
}
};
// 上传图片的时候加上这两行:
options.headers['X-Forwarded-For'] = getRandomIP();
fileUpload(file); // console.log('可以压缩:' + file);
完整的代码原作者已经在知乎、Github 上开源了,感兴趣的可以自行尝试。
同时,作者也发布到了 npm 上面。只需要全局安装:
pnpm i super-tinypng -g
然后在你想要压缩图片的目录里面运行 super-tinypng
就能自动压缩图片了,并且不会有数量限制!
不过 TinyPNG 在未来是完全有可能将这个办法给封杀的,大家能用就早用早享受。
不受限制的站点
Free TinyPNG https://free.tinypng.site/
这个网站和 TinyPNG 网站几乎一样,不同的是它取消了数量限制,和大小限制。图片超过 5MB,是不能被免费 TinyPNG 的网站服务和 API 接受的。但是在这个站点就可以。
这个网站有宣称自己的算法和 TinyPNG 一致,不过具体有待考证。
API 服务
他们家的 API 服务同样也十分慷慨,对每个账户每个月有 500 次的免费调用次数。更惊喜的是,他们对于主流的代码环境都支持。比如针对于 node.js
他们专门开发了易用的 SDK,实际在最简单的环境中,我们仅需4行代码就可以使用 API 进行图片压缩了。
// npm install tinify
const tinify = require("tinify");
tinify.key = "API_KEY";
const source = tinify.fromFile(imagePath);
await source.toFile(outputPath);
要获取 API_KEY,只需要注册一个账号,然后就能在官网的控制台中获取。
我们可以在控制台看到我们这个月使用的次数。
如果说想批量压缩图片,这里有个 demo 供参考使用,可以直接在 node 环境下运行。
const fs = require('fs');
const path = require('path');
const tinify = require("tinify");
tinify.key = "API_KEY";
const folderPath = path.join(__dirname, '..', 'OLD_FOLDER_NAME'); // 替换为您在外面一级的文件夹路径
const folderName = 'NEW_FOLDER_NAME'; // 新文件夹的名称,会在当前目录生成
const imageCount = 500; // 替换为要压缩的图片数量
async function compressImage(imagePath, outputPath) {
try {
const source = tinify.fromFile(imagePath);
await source.toFile(outputPath);
console.log(`压缩成功:${outputPath}`);
} catch (error) {
console.error(`压缩失败:${imagePath}`, error);
}
};
async function compressImagesInFolder(folderPath, count) {
try {
const imageFiles = fs.readdirSync(folderPath).filter(file => {
const extension = path.extname(file).toLowerCase();
return extension === '.png' || extension === '.jpg' || extension === '.jpeg';
});
const selectedImages = imageFiles.slice(0, count);
for (const imageFile of selectedImages) {
const imagePath = path.join(folderPath, imageFile);
const compressedImagePath = path.join(__dirname, folderName, imageFile); // 修改输出路径
await compressImage(imagePath, compressedImagePath); // 传递输出路径给compressImage函数
}
} catch (error) {
console.error('压缩图片时发生错误:', error);
}
};
function createOutputFolder() {
const folderPath = path.join(__dirname, folderName);
if (!fs.existsSync(folderPath)) {
fs.mkdirSync(folderPath);
}
return folderPath;
};
createOutputFolder();
compressImagesInFolder(folderPath, imageCount);
总结
图片压缩的不同姿势,希望能帮助到仔细看完这篇文章的你。
如果让我说一个总结,我认为在当下(2024.4)我使用这些方法的频率是这样的:
- 如果是在不改变当前图片格式的情况下,我会优先使用 tinypng 进行压缩。毕竟我们有办法无限白嫖它。
- 如果是在项目中使用,那么 imagemin 是我们优先的考量。能集成在 CI/CD 自然是开发的最佳实践之一。
- 如果是针对于博客项目,或者一些图片服务,imagemin 有可能不支持对应的脚手架,那么就会使用命令行。这种时候我会统一将图片转成 webp,这样子用户的体验是最好的。
- 如果是针对于图片需要小幅修改的场景,那么依据需要修改好的范围,我会针对性的使用 Luna Paint 等集成在 VS Code 中的插件服务 > 能云存储的在线服务 > 到 Photoshop 等图片修改工具进行修改。
- 除去以上情形无法解决,我会使用本文提到的部分在线服务。