图片占平均网页总大小的 50%。如果图像没有优化,该站点不仅需要更多的时间来加载,而用户也正在使用更多的数据,这两个问题都可以通过在下载之前优化图像来解决,至少在一定程度上是这样的。

世界各地的研究人员正忙于开发新的图像格式,尽管与 PNG 或 JPG 等其他格式相比尺寸较小,但具有高视觉质量。尽管这些新格式仍处于开发阶段,通常对浏览器的支持有限,但其中一种 WebP 正获得大量关注。虽然它们与光栅图像并不属于同一类,但 svg 是近年来我们许多人使用的另一种格式,因为其固有的轻量级。

我们有很多方法可以制作更小和优化的图像。在本教程中,我们将编写 bash 脚本来创建和优化不同图像格式的图像,目标是最常见的格式,包括JPG、PNG、WebP和SVG。我们的想法是,在我们提供图片之前,先对它们进行优化,这样用户就可以在没有任何字节膨胀的情况下,获得最令人惊叹的视觉体验。

设置

在开始之前,让我们按顺序获取所有依赖项。同样,我们正在编写 Bash 脚本,因此我们将花时间在命令行上。

以下是我们开始优化图像所需的所有依赖项的命令:

linuxmi@linuxmi /home/linuxmi/www.linuxmi.com                                   
⚡ sudo apt-get update

linuxmi@linuxmi /home/linuxmi/www.linuxmi.com
sudo apt-get install imagemagick webp jpegoptim optipng

linuxmi@linuxmi /home/linuxmi/www.linuxmi.com
npm install -g svgexport svgo

在安装svgexport时可能会遇到错误

为了避免这种情况,您将需要以下命令

sudo npm install -g svgexport --unsafe-perm=true

在开始使用之前,最好先了解我们正在使用什么:

注意:强烈建议在继续之前备份您的图像。我们即将运行更改这些图像的程序,虽然我们计划不理会原始图像,但一个错误的命令可能会以某种不可逆转的方式改变它们。

整理图像

好的,我们在技术上设置好了。但在我们开始优化所有东西之前,我们应该稍微整理一下我们的文件。让我们根据它们的 MIME 类型将它们分成不同的子目录来组织它们。事实上,我们可以创建一个新的 bash 来为我们做这件事!

以下代码创建了一个名为linuxmi.com.sh的脚本:

#!/bin/bash

input_dir="$1"

if [[ -z "$input_dir" ]]; then
  echo "请指定输入目录。"
  exit 1
fi

for img in $( find $input_dir -type f -iname "*" );
do
  # 获取图像的类型
  img_type=$(basename `file --mime-type -b $img`)

  # 为图像类型创建一个目录
  mkdir -p $img_type

  # 将图像移动到它的类型目录中
  rsync -a $img $img_type
done

如果您不熟悉编写脚本,这可能看起来令人困惑,但它所做的实际上非常简单。我们给脚本一个输入目录,它会在其中查找图像。然后脚本进入该输入目录,查找图像文件并识别它们的 MIME 类型。最后,它在每个 MIME 类型的输入文件夹中创建子目录,并将每个图像的副本放入它们各自的子目录中。

原来这样的:

让我们运行它!

linuxmi@linuxmi /home/linuxmi/www.linuxmi.com/linuxmi.com                       
⚡ bash linuxmi.com.sh /home/linuxmi/www.linuxmi.com/linuxmi.com/linuxmi

OK。该目录现在看起来像下面截图这样。

现在我们的图像已经组织好了,我们可以继续创建每个图像的变体。我们将一次处理一种图像类型。

转换为 PNG

我们将在本教程中将三种类型的图像转换为 PNG:WebP、JPEG 和 SVG。让我们从编写一个名为 的脚本开始,它的作用:将 WebP 文件转换为 PNG 文件。

 

这将发生了什么:

  • 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 变体。

我们现在开始运行脚本:

linuxmi@linuxmi /home/linuxmi/www.linuxmi.com/linuxmi.com                       
⚡ bash linuxmi.com.sh webp

现在我们在 webp 目录中有了我们的 PNG 图像。接下来,让我们用另一个名为jpg2png.sh的脚本将JPG/JPEG文件转换为PNG:

#!/bin/bash

# 包含图像的目录
input_dir="$1"

if [[ -z "$input_dir" ]]; then
  echo "请指定输入目录。"
  exit 1
fi

# 对于输入目录中的每个jpeg
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”。这指的是逻辑OR,它是一个脚本,它是找到所有扩展名为 .jpg 或 .jpeg 的图像的脚本。

#!/bin/bash

# 包含图像的目录
input_dir="$1"

if [[ -z "$input_dir" ]]; then
  echo "请指定输入目录。"
  exit 1
fi

# 对于输入目录中的每个jpeg
for img in $( find $input_dir -type f -iname "*.jpg" -o -iname "*.jpeg" );
do
  convert $img ${img%.*}.png
done

运行如下命令:

linuxmi@linuxmi /home/linuxmi/www.linuxmi.com/linuxmi.com                       
⚡ bash jpg-png.sh jpeg 

现在我们有了来自 JPG 的 PNG 变体,我们也可以对 SVG 文件做同样的事情:

#!/bin/bash

# 包含图像的目录
input_dir="$1"

# png image width
width="$2"

if [[ -z "$input_dir" ]]; then
  echo "请指定输入目录。"
  exit 1
elif [[ -z "$width" ]]; then
  echo "请指定图像宽度。"
  exit 1
fi

# 对于输入目录中的每个 svg
for img in $( find $input_dir -type f -iname "*.svg" );
do
  svgexport $img ${img%.*}.png $width:
done

这个脚本有一个新功能。由于 SVG 是一种可缩放格式,我们可以指定width指令来放大或缩小 SVG。我们使用svgexport之前安装的包将每个 SVG 文件转换为 PNG:

linuxmi@linuxmi /home/linuxmi/www.linuxmi.com/linuxmi.com                       
⚡ bash svg-png.sh svg+xml 800

我们现在已经做了很多伟大的工作,在其他图像格式的基础上创建了一堆 PNG 文件。在真正优化图像格式之前,我们仍然需要对其他图像格式做同样的事情。

转换为 JPG

跟随 PNG 图像创建的脚步,我们将 WebP、JPEG 和 SVG 转换为 JPG。让我们从编写一个png-jpg.sh将 PNG 转换为 SVG 的脚本开始:

#!/bin/bash

# 包含图片目录
input_dir="$1"

# jpg 图像质量
quality="$2"

if [[ -z "$input_dir" ]]; then
  echo "请指定输入目录。"
  exit 1
elif [[ -z "$quality" ]]; then
  echo "请指定图像质量。"
  exit 1
fi

# 用于输入目录中的每个PNG
for img in $( find $input_dir -type f -iname "*.png" );
do
  convert $img -quality $quality% ${img%.*}.jpg
done

您现在可能已经注意到这些脚本中的一种模式。但是这个引入了一种新的功能,我们可以设置一个-quality指令来将 PNG 图像转换为 JPG 图像。其余的都一样。

下面是我们如何运行它:

linuxmi@linuxmi /home/linuxmi/www.linuxmi.com/linuxmi.com                       
⚡ bash png-jpg.sh png 90

OK。现在我们的 png 目录中有了 JPG 图像。让我们用 webp-jpg.sh 脚本做同样的事情:

然后将PNG转换为JPG#!/bin/bash

# 包含图片目录
input_dir="$1"

# jpg 图像质量
quality="$2"

if [[ -z "$input_dir" ]]; then
  echo "请指定输入目录。"
  exit 1
elif [[ -z "$quality" ]]; then
  echo "请指定图像质量。"
  exit 1
fi

# 用于输入目录中的每个webp
for img in $( find $input_dir -type f -iname "*.webp" );
do
  # 先转换成png
  dwebp $img -o ${img%.*}.png

  # 然后将PNG转换为JPG
  convert ${img%.*}.png -quality $quality% ${img%.*}.jpg
done

同样,这与我们为将 WebP 转换为 PNG 编写的内容相同。但是,有一个波折。我们无法将 WebP 格式直接转换为 JPG 格式。因此,我们需要在这里获得一点创意,将 WebP 转换为 PNG 使用dwebp然后将 PNG 转换为 JPG 使用convert. 这就是为什么在for循环中,我们有两个不同的步骤。

现在,让我们运行它:

linuxmi@linuxmi /home/linuxmi/www.linuxmi.com/linuxmi.com                       
⚡ bash webp-jpg.sh webp 90

我们为 WebP 图像创建了 JPG 变体。现在让我们将 SVG 处理为 JPG:

#!/bin/bash

# 包含图像的目录
input_dir="$1"

# jpg图像宽度
width="$2"

# jpg图像质量
quality="$3"

if [[ -z "$input_dir" ]]; then
  echo "请指定一个输入目录。"
  exit 1
elif [[ -z "$width" ]]; then
  echo "请指定图像宽度。"
  exit 1
elif [[ -z "$quality" ]]; then
  echo "请指定图像质量。"
  exit 1
fi

# 对于输入目录中的每个svg
for img in $( find $input_dir -type f -iname "*.svg" );
do
    svgexport $img ${img%.*}.jpg $width: $quality%
done

你可能会认为你以前看过这个脚本。是的!我们使用相同的脚本从SVG创建PNG图像。这个脚本的唯一补充是,我们可以指定JPG图像的质量指令。

linuxmi@linuxmi /home/linuxmi/www.linuxmi.com/linuxmi.com                       
⚡ bash svg-jpg.sh svg+xml 512 90

转换为 WebP

WebP 是一种为现代浏览器设计的图像格式。在撰写本文时,它享有大约 90% 的全球浏览器支持,包括在 Safari 中的部分支持。WebP 的最大优势是与其他图像格式相比,它的文件大小要小得多,而且不会牺牲任何视觉质量。这使它成为为用户服务的好格式。

讨论已经足够了。现在让我们写一个png-webp.sh——你猜对了——用 PNG 文件创建 WebP 图像:

#!/bin/bash

# 包含图片目录
input_dir="$1"

# webp图像质量
quality="$2"

if [[ -z "$input_dir" ]]; then
  echo "请指定输入目录。"
  exit 1
elif [[ -z "$quality" ]]; then
  echo "请指定图像质量。"
  exit 1
fi

# 对于输入目录中的每个webp
for img in $( find $input_dir -type f -iname "*.png" );
do
  cwebp $img -q $quality -o ${img%.*}.webp
done

这与我们用来从 WebP 文件创建 PNG 图像的脚本正好相反。我们不使用dwebp,而是使用cwebp

bash png-webp.sh png 90

我们有我们的 WebP 图像。现在让我们转换 JPG 图像。棘手的是没有办法直接将 JPG 文件转换为 WebP。因此,我们将首先将 JPG 转换为 PNG,然后在我们的jpg-webp.sh脚本中将中间的 PNG 转换为 WebP:

#!/bin/bash

# 包含图片目录
input_dir="$1"

# webp图像质量
quality="$2"

if [[ -z "$input_dir" ]]; then
  echo "请指定输入目录。"
  exit 1
elif [[ -z "$quality" ]]; then
  echo "请指定图像质量。"
  exit 1
fi

# 对于输入目录中的每个webp
for img in $( find $input_dir -type f -iname "*.jpg" -o -iname "*.jpeg" );
do
  # 先转换成png
  convert $img ${img%.*}.png

  # 然后将png转换为webp
  cwebp ${img%.*}.png -q $quality -o ${img%.*}.webp
done

现在我们可以像这样使用它来获取JPG 文件的 WebP 变体:

bash jpg-webp.sh jpeg 90

将所有内容合并到一个目录中

现在我们已经完成了转换,我们离优化我们的工作又近了一步。但首先,我们要把所有的图像都带回一个目录,这样就可以用更少的命令轻松优化它们。

下面是创建一个名为的新 bash 脚本的代码combine-images.sh

#!/bin/bash

input_dirs="$1"
output_dir="$2"

if [[ -z "$input_dirs" ]]; then
  echo "请指定输入目录。"
  exit 1
elif [[ -z "$output_dir" ]]; then
  echo "请指定输出目录。"
  exit 1
fi

# 创建一个目录来存储生成的图像
mkdir -p $output_dir

# 将输入目录用逗号分隔的字符串分割成一个数组
input_dirs=(${input_dirs//,/ })

# 输入目录中的每个目录
for dir in "${input_dirs[@]}"
do
  # 将图像从这个目录复制到生成的图像目录
  rsync -a $dir/* $output_dir/
done

第一个参数是以逗号分隔的输入目录列表,它将图像传输到目标组合目录。第二个参数是定义组合目录。

bash combine-images.sh jpeg,svg+xml,webp,png generated-images

优化 SVG

让我们从优化 SVG 图像开始。将以下代码添加到optimize-svg.sh

#!/bin/bash

# 包含图片目录
input_dir="$1"

if [[ -z "$input_dir" ]]; then
echo "请指定一个输入目录。"
exit 1
fi

# 对于输入目录中的每个SVG
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

优化PNG

让我们继续滚动和优化我们的 PNG 文件,使用此代码创建一个optimize-png.sh命令:

#!/bin/bash

# 包含图像的目录
input_dir="$1"

if [[ -z "$input_dir" ]]; then
  echo "请指定输入目录。"
  exit 1
fi

# 用于输入目录中的每个PNG
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通过在图像质量和大小之间进行权衡来玩弄这个论点以找到合适的值。

优化JPG

我们已经到了最后一部分!我们将通过优化 JPG 图像来结束一切。将以下代码添加到optimize-jpg.sh

#!/bin/bash

# 包含图像的目录
input_dir="$1"

# 目标图像质量
quality="$2"

if [[ -z "$input_dir" ]]; then
  echo "请指定一个输入目录。"
  exit 1
elif [[ -z "$quality" ]]; then
  echo "请指定图像质量。"
  exit 1
fi

# 对于输入目录中的每个jpg或jpeg
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

总结

看到了吗?使用一些脚本,我们可以直接从命令行执行重型图像优化,并在任何项目中使用它们,因为它们是全局安装的。我们可以设置 CI/CD 管道来创建每个图像的不同变体,并使用有效的 HTML、API 为它们提供服务,甚至可以设置我们自己的图像转换网站。

我希望你喜欢阅读这篇文章并从中学到一些东西。快乐编码!

发表评论