0%

用 WebP 作为你的首选图片文件格式

WebP 自 2010年9月30日 发布以来,从最初支持的软件寥寥无几,到现在基本上已经全平台可用(没错,说的就是你: Safari!),是时候把 WebP 作为首选图片文件格式了。

先来说说 WebP 的历史:WebP 是一种同时提供了有损压缩与无损压缩(可逆压缩)的图片文件格式,派生于影像编码格式 VP8,被认为是 WebM 多媒体格式的姊妹项目,由 Google 在购买 On2 Technologies 后发展而来,以 BSD 协议授权发布。

其中的关键点在于,在同等画质下,文件大小相对比其他图片文件格式要小得多。那么这对于图片在网络上传输来说,将耗费更少的时间,节省宝贵的带宽资源。

但是,尽管大部分浏览器都已经兼容了 WebP,但仍有少部分没有兼容(没错,还是在说你: Safari!)。所以在使用 WebP 作为网站图片资源的时候,需要做「向后兼容」。「向后兼容」的方法可以由前端完成,也可以由后端完成,两种方法各有各的优点和缺点,就看你最终的应用场景适合于哪一种。

如何转换现有图片到 WebP ?

在 Google 的 WebP 项目 主页 中,提供了用来转换现有图片格式到 WebP 的工具和库。Google 推荐使用 cwebp 作为命令行工具来进行转换;libwebp 作为 WebP 编码/解码 的 API。

假设你想使用 cwebp 来转换你现有的图片,那么:

1
2
3
4
5
$ cwebp images/flower.jpg -o images/flower.webp # 转换单个文件

$ cwebp -q 50 images/flower.jpg -o images/flower.webp # 以 50% 的质量转换单个文件

$ for file in images/*; do cwebp "$file" -o "${file%.*}.webp"; done # 转换整个目录下的文件

更多的参数使用方法可以通过 cwebp -longhelp 查看。

如何给 WebP 做向后兼容?

就在昨天,我把我博客所有的图片都转换为了 WebP 格式。由于博客是 Hexo 生成的纯静态文件,所以我选择了在 NGINX 里做「向后兼容」这个操作。

大题思路是:

  • 根据 HTTP 请求头中的信息判断浏览器是否兼容 WebP
  • 如果兼容,返回 WebP 文件;如果不兼容,则返回原有图片

有了思路之后就可以开始改 NGINX 的配置文件了:

1
2
3
4
map $http_accept $webp_ext {
default "";
"~*webp" ".webp";
}

首先根据 HTTP 请求头来判断是否支持 WebP,如果支持,则 $webp_ext 会被替换为 .webp;如果不支持,则 $webp_ext 为空。

1
2
3
4
map $uri $file_ext {
default "";
"~(\.\w+)$" $1;
}

把文件名和扩展名拆分,以便在后续使用。以上两个配置需要写在 http 配置中,而不是 serverlocation

1
2
3
4
5
6
7
server {
...
location ~* "^(?<path>.+)\.(png|jpeg|jpg|gif)$" {
try_files $path$webp_ext $path$file_ext =404;
}
...
}

匹配所有的图片文件后缀,如果浏览器兼容 WebP,则返回 WebP 格式的图片,如果不存在则会返回原有图片格式的文件。

这样,就算做好了对于浏览器 WebP 支持的「向后兼容」。但是这并不是一个完美的方案:

  • 优点:
    • 只需要将现有图片转换为 WebP 格式
    • 无需编写任何前端代理
    • NGINX 配置文件改动十分方便简洁
  • 缺点:
    • 需要保存相同图片的两种不同格式,占用了更多的硬盘空间
    • 对于不按照 MIME Type 而按照扩展名缓存文件的 CDN 来说,因为这种方式只是替换了请求的数据而没有更换扩展名,所以 WebP 格式的内容被缓存后,再分发给不支持 WebP 的浏览器就会出现无法显示的问题。所以此时可以考虑让前端来做扩展名替换,再动态的加载,就可以避免这个问题。

虽然我没有做具体的数据分析,但是在相同的网络环境下,在加载图片较多的页面时,使用 WebP 时的加载速度明显要快于使用原有的图片格式,对于浏览体验的改善可以说是相当大了。