大厂都使用webp图片提速降本,必须安排!

456次阅读  |  发布于8月以前

1 背景

站点性能优化一个很重要的方向是图片压缩,一方面能提升客户端访问性能,另外一方面因为图片体积变小,能大大减少站点的流量成本,特别是博客,相比文本内容,大量的图片占了流量成本的大头。

而图片压缩后,webp格式通常比其他常见图片格式如jpg,png等体积小(不是100%一定小),所以没有什么理由不用webp。

之前没有使用webp的原因有几点:

● 并非所有浏览器都支持webp格式,如果要兼容所有浏览器,有点麻烦。

● jpg、png图片压缩比还行,已经减少了不少,尚能接受。

不过好几次看到掘金使用了webp格式图片,想想大厂都用,也安排上吧。

2 掘金图片使用方式

打开开发者工具,查看图片使用方式:

掘金访问图片.png图片链接:

https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bcb9c940a37c4f0ab192094c268f0844~tplv-k3u1fbpfcp-jj-mark:3024:0:0:0:q75.awebp#?w=1199&h=367&s=368599&e=jpg&b=0e0e0e

从链接上大概可以猜到一些参数含义:

序号类型
q75图片压缩质量,满分100
awebp看起来会用到webp,用不用还要根据客户端请求做判断
w原图宽度
h原图高度
e=jpg原图格式,如果客户端浏览器不支持webp,就显示原图格式,而即便是显示原图格式,也应该是压缩后的

3 使用webp图片

3.1 压缩比 比较

使用webp的原因是图片质量尚可,压缩比高于其他图片格式,因此比较一下做到心里有数。

以下图片压缩使用的工具是:tinify[1]

比较结果:

图片原始大小原格式压缩webp
02获取连接.jpg50 KB30 KB35 KB
03连接池资源.jpg23 KB12 KB10 KB
06池的动态伸缩.jpg14 KB9 KB11 KB
06资源不足.jpg38 KB20 KB15 KB
07状态-state.jpg17 KB9 KB9 KB
08工作线程.jpg32 KB18 KB24 KB
evict状态.jpg7 KB14 KB4 KB
jvmparameter.jpg68 KB30 KB10 KB
弱引用.jpg236 KB81 KB34 KB
资源相关类.jpg10 KB6 KB9 KB
资源状态一.jpg9 KB6 KB9 KB
累计大小504 KB235 KB170 KB

从这个结果可以看到:

3.2 压缩工具比较

webp是谷歌推行的,并有配套的图片转换工具cwebp和gif2webp,参考:谷歌webp使用入门[2]

比较结果:

图片原始大小cwebptinify
02获取连接.jpg50 KB36 KB35 KB
03连接池资源.jpg23 KB11 KB10 KB
06池的动态伸缩.jpg14 KB11 KB11 KB
06资源不足.jpg38 KB16 KB15 KB
07状态-state.jpg17 KB9 KB9 KB
08工作线程.jpg32 KB25 KB24 KB
evict状态.jpg7 KB4 KB4 KB
jvmparameter.jpg68 KB11 KB10 KB
弱引用.jpg236 KB35 KB34 KB
资源相关类.jpg10 KB9 KB9 KB
资源状态一.jpg9 KB9 KB9 KB
累计大小504 KB176 KB170 KB

注:这里cwebp的压缩质量设置和掘金一样,为75,这也是cwebp命令的默认值。

可以看到,cweb的压缩比和tinify的压缩比差不多,再对比实际图片效果,肉眼也难以分辨出来,这么来看,花307.52元买的tinify年费,并不是很划算,好在tinify除了转换webp还能压缩原格式。

3.3 兼容处理

webp图片并非所有浏览器都支持,因此要做兼容处理。

3.3.1 客户端兼容

客户端可以通过如下方式兼容:

<picture>
  <source type="image/webp" srcset="/images/02sourcecode/01hikaricp/webp/02获取连接.webp">
  <img src="/images/02sourcecode/01hikaricp/compress/02获取连接.jpg">
</picture>

同时准备两个图片,如果浏览器支持就会显示source中的图片,否则会显示img中的图片。

不过这种方式对于一个图文发布系统,操作不是很友好,因为不太可能让发布者手动去添加这样的内容。

如果是个人博客,就自己一个人去发布文章,那还能接受。

3.3.2 服务端兼容

兼容处理流程大致如下:

webp图片优化流程.png

3.3.2.1 图片上传流程

● 存储原始图片

这一步不是必须的,如果使用原始格式压缩图片,原始图片实际可以不要。

● 压缩并存储图片

一般就是压缩为两种格式,原始格式和webp格式。

图片文件存储可以放到类似S3之类的分布式对象存储库中,这样方便分布式部署的web server访问。

● 生成图片链接

这个图片链接需要包含可以推导出最终要访问的图片的相关信息,例如图片格式,图片大小等等。

图片上传流程不是支持webp新引入的技术,具体实现不再展开描述。

3.3.2.1 图片请求流程

● 判断是否支持webp

服务端可以根据客户端请求头中的"Accept"来判断是否支持webp:

代码参考如下:

 private boolean isSupportWebp() {
        String header_accept = request.getHeader("Accept");
        if (header_accept != null && header_accept.indexOf("image/webp") > -1) {
            return true;
        } else {
            return false;
        }
    }

● 根据图片链接获取对应图片路径

这里仅示例用,图片链接为:

http://10.0.0.8:8042/api/requestImg?imageId=获取连接&sourceImageType=jpg
参数描述
imageId图片标识,用于获取正确的图片
sourceImageType原始图片类型,如果不支持webp,就使用原始图片类型的压缩图片

代码参考,这里仅是简单从服务器classpath获取图片:

private static final String IMG_HOME_PATH = "/static/images/02sourcecode/01hikaricp";
    public String getImageFilePath(String imageId, String sourceImageType) {
        String filePpath = "";
        if (isSupportWebp()) {
            filePpath = IMG_HOME_PATH + "/webp/" + imageId + ".webp";
        } else {
            filePpath = IMG_HOME_PATH + "/compress/" + imageId + "." + sourceImageType;
        }
        return filePpath;
    }

● 返回图片

基本思路是根据请求参数和浏览器请求头信息,设置对应的响应头信息和返回图片,代码参考如下:

 @RequestMapping("/api/requestImg")
    public void requestImg(@RequestParam("imageId") String imageId, @RequestParam("sourceImageType") String sourceImageType) throws IOException {
        // 可以设置缓存,使用这种方式时,需要注意每次重新上传相同图片,链接地址要变化,否则下次还是会使用缓存图片
        response.setHeader("Cache-Control", "max-age=3153600");

        if (isSupportWebp()) {
            response.setContentType("image/webp");
        } else {
            response.setContentType("image/" + sourceImageType);
        }

        String filePath = getImageFilePath(imageId, sourceImageType);
        ClassPathResource resource = new ClassPathResource(filePath);
        try (InputStream in = resource.getInputStream(); OutputStream os = response.getOutputStream()) {
            byte[] b = new byte[1024];
            while (in.read(b) != -1) {
                os.write(b);
            }
            os.flush();
        }
    }

● 性能验证

通过以上步骤,看起来已经支持服务端动态返回webp图片,但这种方式是先读取浏览器可支持的图片,然后再将图片以流的方式返回给客户端,虽然能返回webp图片,但是性能和直接访问webp图片链接一样么?

以下是远程网站测试结果:

序号类型性能
第一行原始jpg图片94 ms
第二行压缩后jpg图片33 ms
第三行压缩后webp图片43 ms
第四行动态返回webp图片37 ms

可以看到基本是图片体积越大越耗时。

需要注意的是:如果网络不稳定会极大影响测试结果,有时候会出现动态返回的webp图片耗时大大超出静态图片耗时,因此以上结果仅供参考。

测试方法:为了避免互相干扰,以上测试结果是通过4个独立页面分别去验证一种获取图片方式,各个页面内容如下:

// 原始图片
<img src="/images/02sourcecode/01hikaricp/02获取连接.jpg">

// 压缩后原格式图片
<img src="/images/02sourcecode/01hikaricp/compress/02获取连接.jpg">

// webp图片
<picture>
  <source type="image/webp" srcset="/images/02sourcecode/01hikaricp/webp/02获取连接.webp">
  <img src="">
</picture>

// 动态获取webp图片
<img src="http://mysite.com/api/requestImg?imageId=02获取连接&sourceImageType=jpg">

4 总结

● webp图片格式的体积小于压缩后的主流图片格式,同时图片质量可以满足非高清要求的站点,而图片体积减小对于提升性能和降低流量成本均有很大帮助,值得使用。

● 通过压缩工具对比,说明选择合适工具的重要性,收费工具性价比未必就一定高。

● 通过客户端和服务端兼容方式对比,可以决定什么场景使用哪种方式。

● 以mvp验证了动态获取方式流程能通,生产环境需要考虑图片存储在什么地方,例如使用对象存储数据库S3;另外生产环境要在网络稳定情况下做好性能测试,验证动态获取方式实际是否存在性能问题,避免做无用功。

注意:以上动态获取webp图片方法并非唯一方式,nginx或cdn也都可以实现,可以根据实际情况选择。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8