配置 WebP 与 AVIF
先看看您的服务器能做什么
在“图片”区域有一张卡片“本服务器的能力”。它显示的不是纸面上写着什么,而是在运行中的 PHP 上实测到的结果:GD 图片库及其版本、读取 JPEG、读取 PNG、读取 WebP、写入 WebP、写入 AVIF。检查会做两遍——函数必须存在,格式也必须出现在 imagetypes() 中。这一点很重要,因为有些主机商编译 GD 时不带 WebP 或 AVIF:那样 imagewebp() 是存在的,直到调用时才会失败。
缺少某个格式时,对应的开关仍然可见,但处于关闭状态并附上原因。对 WebP 来说是:GD 必须带 libwebp 编译。对 AVIF:PHP 8.1 或更新版本并带 libavif。这两件事都归您的主机商管。绕道通过 AJAX 调用也没有用——服务器会明确拒绝启用一个不可用的格式。
“Imagick”这一行纯属信息。本模块并不使用 Imagick;那里写着“不存在”时,您并不缺什么。
启用并设定这三个数值
一共有三个开关和三个数值字段。“生成 WebP”和“生成 AVIF”默认关闭,“通过 <picture> 送出”是开启的。这样分开是有意的:您可以先让这些文件生成出来,之后再把送出打开。反过来也一样:把送出关掉之后,文件仍然会继续生成,但发给访客的是未经改动的标记。
WebP 质量为 82,AVIF 质量为 50。两个字段都接受 30 到 100 的值,超出这个范围的会被裁到区间内。两个默认值不同并不是疏忽:AVIF 的度量方式与 JPEG 不同,50 在那里就已经非常干净了。超过 90 之后,WebP 的文件增长会快过肉眼可见的收益。
百万像素上限为 40,可取 4 到 400 之间的值。它防的是内存上限:一张 6000 × 4000 的照片在解码时约占 96 MB,与它的文件有多大无关。此外,Kakapo 在每次解码前都会对照实际还剩下的内存来计算,宁可跳过,也不让上传死在上限上。
您改动这三个开关中的任何一个,已存放的页面缓存都会被丢弃。质量和百万像素上限则会让它留着——它们要到下一次上传时才起作用,不会让任何已存放的页面变得不对。
上传时会发生什么
转换在上传的末尾进行,也就是 WordPress 生成了全部图片尺寸之后。被处理的是元数据中的那个文件以及每一个生成出来的尺寸——而不是核心保留的原始文件,它本来也从不对外送出。
每个派生文件都作为伴随文件放在它的原图旁边,并带着原图的完整名称加上后缀:bild.jpg 会变成 bild.jpg.webp,而不是 bild.webp。原因:同一个目录里允许 bild.jpg 和 bild.png 并存;如果只是替换后缀,其中一个派生文件就会把另一个覆盖掉。目标位置上已经有东西时,不会去动它。
写入时会先写到一个文件名中带进程号和随机数的相邻文件。只有当实测它比原图更小时,才会通过 rename 挪到自己的位置。如果它更大或一样大,就会被删除,仍旧保留原图。因此中途被中断的运行绝不会留下一个过大的派生文件。
上传有 12 秒的时间预算。AVIF 在生成时比 WebP 慢上好几倍,六个尺寸乘以两种格式,否则就会撞上网页服务器的执行时间——运营者会看到一次中断的上传。放不下的部分由一次性的补充运行来完成:它会被安排在 30 秒之后,届时有 90 秒的时间。已完成的和被跳过的都会记在附件上,以免每次重新生成时都重复同样的尝试。
对于 JPEG,会把 EXIF 中的旋转信息补上,因为 GD 生成的 WebP 和 AVIF 不会带上这一项。对于 PNG 原图,Kakapo 会先尝试无损 WebP——徽标、图形和截图这样往往会更小,而且边缘不会毛糙。透明度在两种目标格式中都会保留。
如何送出
Kakapo 会把 <img> 包进一个 <picture> 里,并在它前面放上 AVIF 和 WebP 源,AVIF 在前——浏览器会取用它能理解的第一个源。这个 <picture> 会被设为 display:contents,以免它在您主题的 Flex 和 Grid 布局中多塞进一个盒子。
这是刻意不走 .htaccess 那条路的选择:内容协商需要 Apache 规则,在 nginx 上完全不起作用,而且会与不按 Accept 头做区分的 CDN 缓存发生冲突。用 <picture> 则由浏览器来决定,一行服务器配置都不需要。
处理的是文章内容和特色图片,优先级为 22——在 Lazy-Load(20)之后、在 CDN 改写(25)之前,好让新产生的源也走 CDN。已经存在的 <picture> 块不会被动到,来自外部主机的图片以及 uploads 目录之外的一切也一样。
有一条规则值得知道:只有当 srcset 中最大的那个版本已有派生文件时,才会提供该格式。缺了中等或较小的版本,浏览器就会取更大一号的——多几 KB,但绝不会出现模糊的图片。而如果缺的是最大的那个,宽屏幕就再也拿不到完整分辨率了;正是这种情况被排除掉了。
如果什么都没产生:读一读原因
卡片“已生成的文件与实际节省”会按格式显示文件数量、原图的总和、派生文件的总和以及两者的差值。每一个数字都来自生成那一刻两个实际文件大小的比较——没有任何估算或推算。这里的“原图”只是那些确实生成出了派生文件的文件的总和,而不是您的整个媒体库。
下面写的是哪些内容没有被转换,以及原因。最重要的几条:
“结果比原图更大”——这不是错误。对于非常小的缩略图,AVIF 经常会这样;原图保留,其余尺寸照样会得到自己的派生文件。
“无法写入”——请检查 uploads 目录的权限。这是唯一一条真正需要您出手的原因。
“目标位置已存在文件”——它没有被覆盖。它多半来自对现有文件的批量运行,而且正是这里本该生成的东西。
“放不进内存”——请调高百万像素上限或 PHP 内存上限。
“无法读取”——文件损坏、CMYK-JPEG 或某种少见的变体。“不是 JPEG/PNG/WebP”——GIF 动画和 PDF 是刻意不去碰的。
如果这张卡片是空的,请启用 WebP 并上传一张图片。已经在媒体库里的图片不会通过这种方式被处理,而要通过同一页面下方的批量运行。