本文约 5,216 字,预计阅读 14 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:加了尺寸,前台还是加载 2000px 原图
列表页要显示 400×260 的卡片图,于是在 functions.php 里加了 add_image_size( 'card', 400, 260, true ),模板里改写成 the_post_thumbnail( 'card' )。刷新一看,图片确实是 400 宽——但打开网络面板,下载的是 2000×1300 的原图,页面上三十张卡片总共 8MB,移动端直接卡死。
原因很简单:这些图片是三个月前上传的,add_image_size 只对之后上传的图生成新尺寸。老图没有对应的文件,the_post_thumbnail() 找不到就退回原图。
原理分析:裁剪发生在上传那一刻
WordPress 的媒体处理流程是:
上传原图 → wp_generate_attachment_metadata()
→ wp_getimagesize() 读取尺寸
→ 遍历所有已注册尺寸(含核心的 thumbnail/medium/medium_large/large)
→ 逐个用 WP_Image_Editor(GD 或 Imagick)裁剪
→ 生成 文件名-宽x高.jpg,写入 _wp_attachment_metadata
关键点有三个:
- 尺寸列表是”上传时的快照”。 结果写进
wp_postmeta的_wp_attachment_metadata里,形如:
array(
'width' => 2000,
'height' => 1300,
'file' => '2026/01/photo.jpg',
'sizes' => array(
'thumbnail' => array( 'file' => 'photo-150x150.jpg', 'width' => 150, 'height' => 150 ),
'medium' => array( 'file' => 'photo-300x195.jpg', 'width' => 300, 'height' => 195 ),
'card' => array( 'file' => 'photo-400x260.jpg', 'width' => 400, 'height' => 260 ),
),
)
- 核心内置四个尺寸,可在「设置 → 媒体」里改:
thumbnail(150×150)、medium(最大 300)、medium_large(768 宽,无后台设置项)、large(最大 1024)。 - 取图时按名字查 sizes 数组,查不到就退回
large或原图,不会自动生成。
add_image_size() 的第四个参数决定裁剪方式:
| 值 | 行为 |
|---|---|
false(默认) |
等比缩放,最长边不超过设定值 |
true |
硬裁剪到精确尺寸,居中裁 |
array( 'left', 'top' ) |
硬裁剪,指定裁切锚点(left/center/right × top/center/bottom) |
保姆级实操
第 1 步:注册尺寸
add_action( 'after_setup_theme', function () {
add_theme_support( 'post-thumbnails' );
set_post_thumbnail_size( 1200, 675, true ); // 特色图片默认尺寸
add_image_size( 'card', 400, 260, true ); // 卡片:硬裁剪居中
add_image_size( 'card-wide', 800, 300, true ); // 宽卡:硬裁剪居中
add_image_size( 'hero', 1920, 720, array( 'center', 'top' ) ); // 头图:锚点靠上
add_image_size( 'avatar-lg', 240, 240, true );
add_image_size( 'logo-soft', 300, 0 ); // 只限宽,高度自适应
} );
高度为 0 时表示不限高,等比缩放。注册必须挂在 after_setup_theme,因为 add_theme_support( 'post-thumbnails' ) 也在这里。
第 2 步:前台调用
// 列表卡片
if ( has_post_thumbnail() ) {
the_post_thumbnail( 'card', array(
'class' => 'card__img',
'loading' => 'lazy',
'alt' => the_title_attribute( array( 'echo' => false ) ),
) );
}
// 需要拿 URL 自己拼接 srcset 时
$src = wp_get_attachment_image_src( get_post_thumbnail_id(), 'hero' );
printf( '<img src="%s" width="%d" height="%d" alt="">', esc_url( $src[0] ), (int) $src[1], (int) $src[2] );
推荐直接用 wp_get_attachment_image(),它会自动输出 srcset 与 sizes:
echo wp_get_attachment_image(
get_post_thumbnail_id(),
'card',
false,
array( 'class' => 'card__img', 'loading' => 'lazy' )
);
上面这段代码通常写在文章卡片片段里,卡片的拆分与复用方式见get_template_part 拆分复用模板片段。
第 3 步:让后台编辑器能选到新尺寸
默认新尺寸不会出现在「插入媒体」的尺寸下拉框里,需要挂 image_size_names_choose:
add_filter( 'image_size_names_choose', function ( $sizes ) {
return array_merge( $sizes, array(
'card' => '卡片 400×260',
'card-wide' => '宽卡 800×300',
'hero' => '头图 1920×720',
) );
} );
后台「外观 → 自定义」里若也需要选择图片类设置项,控件写法见主题选项面板实现方式对比。
第 4 步:重建缩略图(关键一步)
新尺寸只对之后上传的图生效,老图必须重建。两种做法:
命令行(推荐,速度快、可断点):
# 重建全部
wp media regenerate --yes
# 只重建缺了 card 尺寸的图(WP-CLI 2.x 支持 --image_size)
wp media regenerate --image_size=card --yes
# 只重建指定附件
wp media regenerate 123 456 --yes
后台图形界面:装 Regenerate Thumbnails(作者 Alex Mills)插件,在「工具 → Regenerate Thumbnails」里点开始。媒体库超过一万张时,插件会分批执行,需要保持页面打开,容易超时,这种情况一律用 WP-CLI。
重建只会新增缺失的尺寸,不会删除已有文件,所以反复执行是安全的。
第 5 步:核对生成结果
wp eval '$m = wp_get_attachment_metadata( 123 ); print_r( array_keys( $m["sizes"] ) );'
wp post meta get 123 _wp_attachment_metadata
文件系统侧同步确认:
ls -lh wp-content/uploads/2026/01/ | grep photo-
第 6 步:清理废弃尺寸
删掉某个尺寸的注册代码后,已生成的文件不会自动消失,会长期占着磁盘。清理办法是先解除注册再删文件与元数据:
add_filter( 'intermediate_image_sizes_advanced', function ( $sizes ) {
unset( $sizes['card-wide'] ); // 阻止后续上传再生成
return $sizes;
} );
# 找出所有 card-wide 文件并确认后删除(先 ls 确认,再 rm)
find wp-content/uploads -name "*-800x300.*" -print
find wp-content/uploads -name "*-800x300.*" -delete
wp media regenerate --yes # 顺便刷新元数据
常见踩坑
- 注册尺寸后没重建。 老图继续加载原图,页面体积暴涨,这是最高频的失误。
- 硬裁剪
true用在含透明通道的 PNG 上。 裁剪区域外的透明像素会被填充成白底或黑底,视WP_Image_Editor实现而定。Logo 类图片应改用不裁剪的等比尺寸。图片地址输出时必须走esc_url(),详见输出转义规范 esc_html esc_attr esc_url。 - 尺寸改了但文件名没变。 把
card从 400×260 改成 420×280 后,老文件仍是photo-400x260.jpg,元数据与新尺寸对不上,必须重新wp media regenerate。 - 原图比设定尺寸小。 上传一张 300px 宽的图,系统不会放大生成 400 宽的
card,元数据里就没有这一项,前台会退回原图。模板里要有兜底逻辑。 add_image_size写在init上。 太晚,上传流程读不到。统一挂after_setup_theme。- 案例: 某站给移动端专门加了
mobile-thumb尺寸,跑完wp media regenerate后磁盘占用从 3.2GB 涨到 4.8GB。原因是历史上有六种旧尺寸(三个废弃主题留下的)从未清理,重建时它们也被一并重新生成。后来先用intermediate_image_sizes_advanced过滤掉废弃尺寸、删除对应文件,再重建,最终降到 2.6GB。教训是:重建之前先梳理一遍当前注册的尺寸清单:
wp eval 'print_r( get_intermediate_image_sizes() );'
- 对象存储(OSS/COS)上的图片重建超时。 远程裁剪需要先把原图下载到本地再上传,网络慢时批量重建极易中断。建议分批:
wp media regenerate --yes配合按月份目录拆分执行。
总结
add_image_size 的完整流程是”注册 → 调用 → 后台可选 → 重建 → 核对”,中间任何一步省略都会导致前台加载错误尺寸。判断标准:新增尺寸后必须跑一次 wp media regenerate,删除尺寸后必须清理残留文件;图片量大的站优先用 WP-CLI 而非后台插件;每次调整尺寸数值都要重建,因为文件名里带着宽高。最后用 wp_get_attachment_image() 输出,让核心自动处理 srcset,移动端就不必下载桌面端大图。
