WordPress 阿里云 OSS 图床:用 x-oss-process 实时生成 WebP 缩略图,流量降 90%-第三维度MokIII

结论先给

把列表卡片图的地址后面追加一个 x-oss-process 参数,交给 OSS 实时裁切并转 WebP:

?x-oss-process=image/auto-orient,1/resize,w_450,h_300,m_fill/sharpen,100/format,webp/quality,q_85

本站实测(首页 13 张卡片图,源图多为 PNG 截图):

体积
原图约 2.27 MB
处理后约 0.20 MB
降幅约 90%

单张对比更直观:一张 265 KB 的 PNG 截图,只裁切是 240 KB(几乎白做),裁切 + 转 WebP 后是 13.8 KB。这个幅度和源图格式有关:PNG 截图能省 95% 以上;如果源图本身已经是 JPEG,压缩空间小得多,实测同一张 JPEG 约省 85%。

真正的收益在 WebP,只裁不转基本等于没优化。

一、为什么要单独做缩略图

很多站的做法是:上传一张图,正文用原图,列表卡片也用原图——只是用 CSS 把它缩小显示。浏览器该下载的字节一个没少。

一张 1200×800 的 PNG 截图动辄 200 KB 以上,首页 13 张就是 2 MB 起步。移动端用户在这些图上花的流量和时间,比 HTML/CSS/JS 加起来还多。

正确做法是:列表用小图,正文用大图,各取所需。

二、x-oss-process 是什么

它是阿里云 OSS 的图片处理服务,在 URL 后追加参数,OSS 就会在读取时实时处理并返回结果:

  • 不需要预先在控制台「数据处理 > 图片处理」里新建样式;
  • 不涉及样式分隔符(!)的那些麻烦;
  • 处理结果 OSS 会自动缓存,同一张图第二次访问不再重复计算。

参数逐段解释:

参数作用
image/auto-orient,1按 EXIF 自动旋转(手机竖拍照片必备)
resize,w_450,h_300,m_fill裁成 450×300。m_fill = 先等比缩放再居中裁切,不变形,和列表卡片的 3:2 比例一致
sharpen,100缩放后轻度锐化,避免糊
format,webp输出 WebP
quality,q_85质量 85,肉眼与原始基本无差

为什么是 450×300:卡片显示宽度约 450px(2 倍屏下 225 CSS px),3:2 比例。尺寸要贴着实际显示尺寸取,取大了等于白省。

三、在哪一层做?三个方案对比

方案说明问题
上传时用 WordPress 生成缩略图用 add_image_size()图存在 OSS、本地不留文件时,缩略图也要占 OSS 空间和费用;尺寸改了要重新生成历史图
用 OSS 控制台建图片样式预先定义样式,?x-oss-process=style/xxx多一点控制台配置;样式与应用代码耦合,迁移时要重建
渲染层追加参数(推荐)输出 HTML 时给 URL 补参数需要一处代码;但零存储成本、改参数即时生效、回滚只删文件

我用第三种:不改数据库、不改主题文件,只在输出层动手。

四、代码:一个 mu-plugin

放到 wp-content/mu-plugins/ 下即可,不需要激活:

<?php
/**
 * Plugin Name: 列表缩略图走 OSS 处理(3:2 裁切 + WebP)
 */

defined( 'ABSPATH' ) || exit;

function my_oss_thumb_process() {
	return 'image/auto-orient,1/resize,w_450,h_300,m_fill/sharpen,100/format,webp/quality,q_85';
}

function my_oss_thumb_hosts() {
	return array( 'img.mokiii.com' );   // 你的图床域名
}

function my_oss_thumb_url( $url ) {
	$url = trim( (string) $url );
	if ( '' === $url ) {
		return $url;
	}
	// 已经带查询串就原样返回,避免 ? 与 & 拼接歧义
	if ( false !== strpos( $url, '?' ) ) {
		return $url;
	}

	$host = strtolower( (string) wp_parse_url( $url, PHP_URL_HOST ) );
	if ( '' === $host || ! in_array( $host, my_oss_thumb_hosts(), true ) ) {
		return $url;
	}

	// gif 转 WebP 会丢动画;svg 是矢量不需要处理
	$ext = strtolower( pathinfo( (string) wp_parse_url( $url, PHP_URL_PATH ), PATHINFO_EXTENSION ) );
	if ( in_array( $ext, array( 'gif', 'svg' ), true ) ) {
		return $url;
	}

	return $url . '?x-oss-process=' . my_oss_thumb_process();
}

/**
 * 只重写 media-3x2 容器内那一个 data-src。
 * 中间的 (?:(?!</div>).)*? 保证不越过容器边界匹配到相邻卡片。
 */
function my_oss_thumb_rewrite( $html ) {
	if ( ! is_string( $html ) || '' === $html || false === strpos( $html, 'media-3x2' ) ) {
		return $html;
	}

	return preg_replace_callback(
		'#(<div\s+class="media\s+media-3x2[^"]*"[^>]*>(?:(?!</div>).)*?data-src=")([^"]+)(")#is',
		function ( $m ) {
			return $m[1] . my_oss_thumb_url( $m[2] ) . $m[3];
		},
		$html
	);
}

function my_oss_thumb_start_buffer() {
	if ( is_admin() && ! wp_doing_ajax() ) {
		return;
	}
	if ( is_feed() || is_robots() ) {
		return;
	}
	ob_start( 'my_oss_thumb_rewrite' );
}
add_action( 'template_redirect', 'my_oss_thumb_start_buffer', 1 );
add_action( 'wp_ajax_nopriv_ajax_load_posts', 'my_oss_thumb_start_buffer', 1 );
add_action( 'wp_ajax_ajax_load_posts', 'my_oss_thumb_start_buffer', 1 );

几个设计要点:

  1. 作用范围刻意收窄——只改列表卡片容器的图片。banner、分类封面、正文头图用的是别的比例(media-3x1、media-21x9),一律按 3:2 裁会严重变形。
  2. 跳过 gif / svg,跳过已带查询串的 URL。
  3. 同时挂整页和"加载更多"的 ajax——后者是分页加载列表的,漏了的话第二页之后的图没处理。
  4. 输出缓冲用 ob_start( 回调 ),不是把整页读进内存再替换。

五、验证

# 看 HTML 里列表图的地址是否带上了参数
curl -sk https://www.mokiii.com/ | grep -o 'x-oss-process=[^"]*' | head -3

# 对比体积(同一张图)
curl -s -o /dev/null -w '原图   %{size_download} B\n' 'https://img.mokiii.com/2026/01/shot.png'
curl -s -o /dev/null -w '处理图 %{size_download} B\n' 'https://img.mokiii.com/2026/01/shot.png?x-oss-process=image/resize,w_450,h_300,m_fill/format,webp/quality,q_85'

第二行应明显小于第一行。也别忘了点一下「加载更多」,确认 ajax 返回的片段里地址同样带参数。

六、要注意的地方

  • 浏览器支持:输出的是 WebP。2020 年后的浏览器都支持(Safari 14+)。若你的访客里有大量老设备,把 format,webp 去掉、只保留裁切。
  • 图床域名白名单:改成你自己的图床域名,不要用通配,避免误伤外链图。
  • 首次访问有处理耗时:OSS 第一次请求会实时计算(通常几十毫秒),之后走 OSS 自身缓存。若你的图会被大量首次访问,可以在上传后就预热一遍。
  • 回滚:删掉这个 mu-plugin 文件即可,前台立刻恢复原图。

一句话总结:列表页用原图是流量黑洞。加一个 x-oss-process 参数,让 OSS 实时裁切 + 转 WebP,零存储成本、改参数即时生效——关键是一定要转 WebP,只裁不转没有意义。