图像占平均网页总大小的 50%。如果图像未经优化,用户最终会下载额外的字节。如果他们下载了额外的字节,网站不仅加载时间更长,而且用户使用更多数据,这两种情况都可以通过在下载之前优化图像来解决,至少可以部分解决。
世界各地的研究人员正在努力开发 新的图像格式,这些格式即使尺寸更小,也能保持高视觉质量,与 PNG 或 JPG 等其他格式相比。尽管这些新格式仍在开发中,并且通常浏览器支持有限,但其中一种格式 WebP 正在引起广泛关注。虽然它们与栅格图像并不完全相同,但 SVG 是近年来许多人一直在使用的另一种格式,因为它们天生轻量级。
我们可以通过多种方法来创建更小、更优化的图像。在本教程中,我们将编写 bash 脚本,以不同图像格式创建和优化图像,以针对最常见的格式,包括 JPG、PNG、WebP 和 SVG。我们的目标是在提供图像之前优化图像,以便用户获得最棒的视觉体验,而不会出现任何字节膨胀。


此 GitHub 仓库 包含我们正在使用的所有图像,欢迎您获取它们并跟随操作。
设置
在我们开始之前,让我们先整理好所有依赖项。同样,我们正在编写 Bash 脚本,因此我们将花时间在命令行中。
以下是我们开始优化图像所需的所有依赖项的命令
sudo apt-get update
sudo apt-get install imagemagick webp jpegoptim optipng
npm install -g svgexport svgo
在我们开始使用它们之前,了解我们正在使用什么是一个好主意
- ImageMagick:适用于各种栅格图像
- webp 优化 WebP 文件
- JPEGoptim 优化 JPG/JPEG 文件
- OptiPNG 优化 PNG 文件
- SVGO 和 svgexport 是优化 SVG 资产的 Node 包
好的,我们的图像位于 GitHub 仓库的 original-images
目录中。您可以在提交 3584f9b
中跟随操作。
注意:强烈建议您在继续操作之前备份您的图像。我们即将运行更改这些图像的程序,虽然我们计划保留原始图像,但一个错误的命令可能会以某种不可逆转的方式更改它们。因此,请备份您计划在实际项目中使用的任何内容,以防止您以后后悔。
整理图像
好的,我们已经从技术上设置好了。但在我们开始优化所有内容之前,我们应该先整理一下我们的文件。让我们根据它们的 MIME 类型将它们分成不同的子目录。实际上,我们可以创建一个新的 bash 来为我们做到这一点!
以下代码创建一个名为 organize-images.sh
的脚本
#!/bin/bash
input_dir="$1"
if [[ -z "$input_dir" ]]; then
echo "Please specify an input directory."
exit 1
fi
for img in $( find $input_dir -type f -iname "*" );
do
# get the type of the image
img_type=$(basename `file --mime-type -b $img`)
# create a directory for the image type
mkdir -p $img_type
# move the image into its type directory
rsync -a $img $img_type
done
如果您不熟悉编写脚本,这可能看起来很令人困惑,但它实际上很简单。我们向脚本提供一个输入目录,它会在其中查找图像。然后脚本进入该输入目录,查找图像文件并识别它们的 MIME 类型。最后,它在输入文件夹中为每个 MIME 类型创建子目录,并将每个图像的副本放到各自的子目录中。
让我们运行它!
bash organize-images.sh original-images
很棒。该目录现在 看起来像这样。现在我们的图像已经整理好了,我们可以继续创建每个图像的不同版本。我们将一次处理一种图像类型。
转换为 PNG
在本教程中,我们将把三种类型的图像转换为 PNG:WebP、JPEG 和 SVG。让我们从编写一个名为 webp2png.sh
的脚本开始,它基本上说明了它的作用:将 WebP 文件转换为 PNG 文件。
#!/bin/bash
# directory containing images
input_dir="$1"
if [[ -z "$input_dir" ]]; then
echo "Please specify an input directory."
exit 1
fi
# for each webp in the input directory
for img in $( find $input_dir -type f -iname "*.webp" );
do
dwebp $img -o ${img%.*}.png
done
以下是发生的事情
input_dir="$1"
:存储脚本的命令行输入if [[ -z "$input_dir" ]]; then
:如果未定义输入目录,则运行后续条件for img in $( find $input_dir -type f -iname "*.webp" );
:循环遍历目录中每个具有.webp
扩展名的文件。dwebp $img -o ${img%.*}.png
:将 WebP 图像转换为 PNG 变体。
我们开始了
bash webp2png.sh webp
我们现在有了我们的 webp
目录中的 PNG 图像。接下来,让我们使用另一个名为 jpg2png.sh
的脚本将 JPG/JPEG 文件转换为 PNG
#!/bin/bash
# directory containing images
input_dir="$1"
if [[ -z "$input_dir" ]]; then
echo "Please specify an input directory."
exit 1
fi
# for each jpg or jpeg in the input directory
for img in $( find $input_dir -type f -iname "*.jpg" -o -iname "*.jpeg" );
do
convert $img ${img%.*}.png
done
这使用的是我们安装的 ImageMagick 包提供的 convert
命令。与上一个脚本一样,我们提供一个包含 JPEG/JPG 图像的输入目录。该脚本在该目录中查找并为每个匹配的图像创建一个 PNG 变体。如果您仔细观察,我们在 find
中添加了 -o -iname "*.jpeg"
。这指的是逻辑或,它是查找所有具有 .jpg
或 .jpeg
扩展名的图像的脚本。
以下是我们运行它的方式
bash jpg2png.sh jpeg
现在我们已经有了 来自 JPG 的 PNG 变体,我们可以对 SVG 文件做同样的事情
#!/bin/bash
# directory containing images
input_dir="$1"
# png image width
width="$2"
if [[ -z "$input_dir" ]]; then
echo "Please specify an input directory."
exit 1
elif [[ -z "$width" ]]; then
echo "Please specify image width."
exit 1
fi
# for each svg in the input directory
for img in $( find $input_dir -type f -iname "*.svg" );
do
svgexport $img ${img%.*}.png $width:
done
此脚本有一个新功能。由于 SVG 是一种可缩放格式,我们可以指定 width
指令来放大或缩小 SVG。我们使用之前安装的 svgexport
包将每个 SVG 文件转换为 PNG
bash svg2png.sh svg+xml
提交 76ff80a
显示了仓库中的结果。
我们在这里做了很多很棒的工作,通过 创建大量基于其他图像格式的 PNG 文件。在我们开始真正优化它们之前,我们还需要对其他图像格式做同样的事情。
转换为 JPG
在创建 PNG 图像的基础上,我们将把 WebP、JPEG 和 SVG 转换为 JPG。让我们从编写一个名为 png2jpg.sh
的脚本开始,该脚本将 PNG 转换为 SVG
#!/bin/bash
# directory containing images
input_dir="$1"
# jpg image quality
quality="$2"
if [[ -z "$input_dir" ]]; then
echo "Please specify an input directory."
exit 1
elif [[ -z "$quality" ]]; then
echo "Please specify image quality."
exit 1
fi
# for each png in the input directory
for img in $( find $input_dir -type f -iname "*.png" );
do
convert $img -quality $quality% ${img%.*}.jpg
done
您现在可能已经注意到这些脚本中的模式。但是,这个脚本引入了一个新的功能,我们可以设置 -quality
指令来将 PNG 图像转换为 JPG 图像。其余部分相同。
以下是我们运行它的方式
bash png2jpg.sh png 90
哇。我们现在有了 我们 png
目录中的 JPG 图像。让我们用 webp2jpg.sh
脚本对 WebP 做同样的事情
#!/bin/bash
# directory containing images
input_dir="$1"
# jpg image quality
quality="$2"
if [[ -z "$input_dir" ]]; then
echo "Please specify an input directory."
exit 1
elif [[ -z "$quality" ]]; then
echo "Please specify image quality."
exit 1
fi
# for each webp in the input directory
for img in $( find $input_dir -type f -iname "*.webp" );
do
# convert to png first
dwebp $img -o ${img%.*}.png
# then convert png to jpg
convert ${img%.*}.png -quality $quality% ${img%.*}.jpg
done
同样,这与我们用于将 WebP 转换为 PNG 的脚本相同。但是,这里有一个转折。我们无法直接将 WebP 格式转换为 JPG 格式。因此,我们需要在这里稍微发挥创意,使用 dwebp
将 WebP 转换为 PNG,然后使用 convert
将 PNG 转换为 JPG。这就是为什么在 for
循环中,我们有两个不同的步骤。
现在,让我们运行它
bash webp2jpg.sh jpeg 90
瞧!我们已经为 WebP 图像创建了 JPG 变体。现在让我们处理 SVG 到 JPG
#!/bin/bash
# directory containing images
input_dir="$1"
# jpg image width
width="$2"
# jpg image quality
quality="$3"
if [[ -z "$input_dir" ]]; then
echo "Please specify an input directory."
exit 1
elif [[ -z "$width" ]]; then
echo "Please specify image width."
exit 1
elif [[ -z "$quality" ]]; then
echo "Please specify image quality."
exit 1
fi
# for each svg in the input directory
for img in $( find $input_dir -type f -iname "*.svg" );
do
svgexport $img ${img%.*}.jpg $width: $quality%
done
您可能认为您之前见过此脚本。您确实见过!我们使用相同的脚本创建了来自 SVG 的 PNG 图像。此脚本的唯一添加是,我们可以指定 JPG 图像的 quality
指令。
bash svg2jpg.sh svg+xml 512 90
我们刚刚完成的所有操作都包含在仓库中的提交 884c6cf
中。
转换为 WebP
WebP 是一种专为现代浏览器设计的图像格式。在撰写本文时,它拥有 约 90% 的全球浏览器支持,包括 Safari 的部分支持。WebP 的最大优势是它比其他图像格式的尺寸要小得多,而不会牺牲任何视觉质量。这使其成为提供给用户的良好格式。
但闲话少说。让我们编写一个 png2webp.sh
,它将——您猜对了——将 PNG 图像创建为 WebP 图像
#!/bin/bash
# directory containing images
input_dir="$1"
# webp image quality
quality="$2"
if [[ -z "$input_dir" ]]; then
echo "Please specify an input directory."
exit 1
elif [[ -z "$quality" ]]; then
echo "Please specify image quality."
exit 1
fi
# for each png in the input directory
for img in $( find $input_dir -type f -iname "*.png" );
do
cwebp $img -q $quality -o ${img%.*}.webp
done
这只是我们用于将 WebP 图像创建为 PNG 图像的脚本的反向操作。我们不是使用 dwebp
,而是使用 cwebp
。
bash png2webp.sh png 90
我们有 我们的 WebP 图像。现在让我们转换 JPG 图像。棘手的是,没有办法直接将 JPG 文件转换为 WebP。因此,我们将首先将 JPG 转换为 PNG,然后在 jpg2webp.sh
脚本中将中间 PNG 转换为 WebP
#!/bin/bash
# directory containing images
input_dir="$1"
# webp image quality
quality="$2"
if [[ -z "$input_dir" ]]; then
echo "Please specify an input directory."
exit 1
elif [[ -z "$quality" ]]; then
echo "Please specify image quality."
exit 1
fi
# for each webp in the input directory
for img in $( find $input_dir -type f -iname "*.jpg" -o -iname "*.jpeg" );
do
# convert to png first
convert $img ${img%.*}.png
# then convert png to webp
cwebp ${img%.*}.png -q $quality -o ${img%.*}.webp
done
现在我们可以这样使用它来获取我们的 JPG 文件的 WebP 变体
bash jpg2webp.sh jpeg 90
提交 6625f26
显示了结果。
将所有内容合并到一个目录中
现在我们已经完成了转换工作,离优化我们的工作又近了一步。但首先,我们将把所有图像放回一个目录中,以便用更少的命令轻松地对其进行优化。
以下代码创建一个名为 combine-images.sh
的新 bash 脚本
#!/bin/bash
input_dirs="$1"
output_dir="$2"
if [[ -z "$input_dirs" ]]; then
echo "Please specify an input directories."
exit 1
elif [[ -z "$output_dir" ]]; then
echo "Please specify an output directory."
exit 1
fi
# create a directory to store the generated images
mkdir -p $output_dir
# split input directories comma separated string into an array
input_dirs=(${input_dirs//,/ })
# for each directory in input directory
for dir in "${input_dirs[@]}"
do
# copy images from this directory to generated images directory
rsync -a $dir/* $output_dir/
done
第一个参数是一个以逗号分隔的输入目录列表,这些目录将把图像传输到目标合并目录。第二个参数定义了合并目录。
bash combine-images.sh jpeg,svg+xml,webp,png generated-images
最终输出可以在 仓库中看到。
优化 SVG
让我们从优化 SVG 图像开始。将以下代码添加到 optimize-svg.sh
中
#!/bin/bash
# directory containing images
input_dir="$1"
if [[ -z "$input_dir" ]]; then
echo "Please specify an input directory."
exit 1
fi
# for each svg in the input directory
for img in $( find $input_dir -type f -iname "*.svg" );
do
svgo $img -o ${img%.*}-optimized.svg
done
我们在这里使用 SVGO 包。它有很多我们可以使用的选项,但为了简单起见,我们只坚持使用优化 SVG 文件的默认行为
bash optimize-svg.sh generated-images

结果可以在提交 75045c3
的仓库中看到。
优化 PNG
让我们继续滚动,使用以下代码来优化我们的 PNG 文件,以创建一个 optimize-png.sh
命令
#!/bin/bash
# directory containing images
input_dir="$1"
if [[ -z "$input_dir" ]]; then
echo "Please specify an input directory."
exit 1
fi
# for each png in the input directory
for img in $( find $input_dir -type f -iname "*.png" );
do
optipng $img -out ${img%.*}-optimized.png
done
在这里,我们使用 OptiPNG 包来优化我们的 PNG 图像。该脚本在输入目录中查找 PNG 图像,并为每个图像创建一个优化版本,并在文件名后添加 -optimized
。有一个有趣的参数 -o
,我们可以用它来指定优化级别。默认值为 2
,**值的范围为 0 到 7。要优化我们的 PNG,我们运行
bash optimize-png.sh generated-images

正如我们所见,OptiPNG 在优化图像方面做得很好。我们可以通过在图像质量和大小之间进行权衡来调整 -o
参数,找到合适的值。在提交 4a97f29
中查看结果。
优化 JPG
我们已经到达了最后的部分!我们将通过优化 JPG 图像来完成所有工作。将以下代码添加到 optimize-jpg.sh
中
#!/bin/bash
# directory containing images
input_dir="$1"
# target image quality
quality="$2"
if [[ -z "$input_dir" ]]; then
echo "Please specify an input directory."
exit 1
elif [[ -z "$quality" ]]; then
echo "Please specify image quality."
exit 1
fi
# for each jpg or jpeg in the input directory
for img in $( find $input_dir -type f -iname "*.jpg" -o -iname "*.jpeg" );
do
cp $img ${img%.*}-optimized.jpg
jpegoptim -m $quality ${img%.*}-optimized.jpg
done
此脚本使用 JPEGoptim。此包的问题是它没有指定输出文件的选项。我们只能就地优化图像文件。我们可以通过首先创建图像的副本,并将其命名为我们喜欢的任何名称,然后优化副本来克服这个问题。-m
参数用于指定图像质量。最好尝试一下,找到质量和文件大小之间的正确平衡。
bash optimize-jpg.sh generated-images 95

结果显示在提交 35630da
中。
总结
看到了吧?用几个脚本,我们可以直接从命令行执行繁重的图像优化,并将其用于任何项目,因为它们是全局安装的。我们可以设置 CI/CD 管道来创建每个图像的不同变体,并使用有效的 HTML、API 来提供它们,甚至设置我们自己的图像转换网站。
我希望您喜欢阅读并从这篇文章中学到一些东西,就像我享受为你们写作一样。编码愉快!
这篇文章有没有 Windows 版本?
大多数使用工具(也许是全部,但我没有检查每一个)都有 Windows 二进制文件,或者可以在 Windows 上运行。Bash 脚本要么需要重写,要么您可以在 WSL 或 cygwin 中使用 bash。也许有人可以分叉仓库,添加与 .sh 文件等效的 .ps1 文件。
哇哦!
我通常使用 https://squoosh.app 进行压缩和转换,但这看起来很棒
只需几行命令即可完成。
这是我想尝试的东西。
感谢您提供这篇很棒的文章。
添加 avif
如果您安装了 Ruby,那么有 https://github.com/toy/image_optim 可以为您完成很多工作