本文约 3,897 字,预计阅读 10 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:硬盘红了,带宽也贵
站点运营两三年后,常见问题:
wp-content/uploads占了 20GB+,云盘要扩容,钱花得冤;- 图片一多,页面加载慢,服务器带宽跑满;
- 换服务器时,几十 GB 图片迁移耗时几小时;
- 想做负载均衡多台服务器,图片同步成了难题。
对象存储(OSS / COS / S3 / 七牛 / 又拍)能一次性解决:按量付费(1GB 一个月几毛钱)、自带 CDN 能力、无限扩容、多机共享。代价是需要插件桥接,且迁移过程要小心。
原理分析:WordPress 是怎么存文件的
WordPress 上传图片的完整链路:
- 浏览器把文件 POST 到
wp-admin/async-upload.php或 REST 的/wp/v2/media; wp_handle_upload()调用wp_upload_dir()拿到目标目录,写入磁盘;- 生成
_wp_attachment_metadata,记录原图和各尺寸缩略图的文件名; - 数据库
wp_posts里插一条attachment记录,guid字段存完整 URL。
要搬到对象存储,需要拦截第 2 步(改写入位置)和第 4 步(改 URL)。这就是为什么必须用插件(或自己写钩子)——WordPress 原生只认本地目录。
两种模式的区别:
| 模式 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 镜像/回源模式 | 文件还在本地,OSS 做镜像,首次访问回源拉取 | 改动最小,服务器仍有一份 | 没省硬盘,只是加速 |
| 直传模式 | 上传到服务器后,钩子把文件推送到 OSS 并(可选)删除本地 | 真省空间 | 依赖插件,迁移要改数据库 URL |
我们要的是直传模式。
保姆级实操
第一步:创建对象存储 Bucket
以阿里云 OSS 为例:
- 地域:选和服务器相同的地域(内网上传免流量费);
- 读写权限:公共读(图片要能被匿名访问);
- 如果用内网:
mimangfei.oss-cn-hangzhou-internal.aliyuncs.com; - 创建 RAM 子账号,只授予
AliyunOSSFullAccess(或更精细的自定义策略),拿 AccessKey。
腾讯云 COS:控制台创建存储桶,权限设为「公有读私有写」,拿 SecretId / SecretKey。
AWS S3:创建 Bucket,关闭 “Block all public access”,设置 Bucket Policy 允许 s3:GetObject,创建 IAM 用户拿 Access Key。
第二步:选插件
| 插件 | 支持厂商 | 特点 |
|---|---|---|
| WP Offload Media(有免费版) | S3、DigitalOcean Spaces、Google Cloud Storage | 国外最主流,免费版够用;国内 OSS 需装兼容性插件或付费版 |
| Aliyun OSS(阿里云官方/第三方) | 仅 OSS | 配置简单,支持内网上传 |
| Tencent Cloud COS | 仅 COS | 腾讯云官方,直传支持好 |
| Media Cloud | S3、Google Cloud、DigitalOcean、Backblaze | 功能强,带图片处理 |
| WPJAM Basic(国人开发) | OSS / COS / 七牛 / 又拍 | 轻量,中文界面,免费 |
推荐组合:国内站用 WPJAM Basic 或厂商官方插件,国外站用 WP Offload Media。
第三步:配置插件(以 WPJAM Basic 为例)
安装后在「WPJAM – 云存储」里填:
云存储服务:阿里云 OSS
Bucket 名称:mimangfei-images
Bucket 地域:oss-cn-hangzhou
AccessKey ID:LTAI********
AccessKey Secret:********
内网上传:开启(服务器在阿里云同地域)
CDN 域名:https://img.mimangfei.com
关键设置:
- 本地保留:建议先开着,迁移验证无误后再关;
- 是否删除本地文件:先不勾,跑一周再决定;
- 图片处理:OSS 支持
?x-oss-process=image/resize,w_800这类参数,可在插件里配置自动 WebP。
第四步:迁移历史文件
插件一般自带”一键同步”功能,但大文件量会超时。更稳的做法是用命令行工具:
# 阿里云 ossutil 同步整个 uploads 目录
ossutil cp -r /var/www/mimangfei/wp-content/uploads/ \
oss://mimangfei-images/wp-content/uploads/ \
--jobs 10 --parallel 5
# 腾讯云 coscli
coscli sync /var/www/mimangfei/wp-content/uploads/ \
cos://mimangfei-1250000000/wp-content/uploads/ -r
# AWS CLI
aws s3 sync /var/www/mimangfei/wp-content/uploads/ \
s3://mimangfei-images/wp-content/uploads/
上传完成后,验证文件数量一致:
# 本地文件数
find /var/www/mimangfei/wp-content/uploads -type f | wc -l
# OSS 文件数
ossutil ls oss://mimangfei-images/ -r | grep -c "Object Number\|:"
第五步:批量替换数据库里的旧 URL
这一步必须做,否则老文章里的图片还是指向本地:
# 先 dry-run 看会改多少条
wp search-replace 'https://mimangfei.com/wp-content/uploads' \
'https://img.mimangfei.com/wp-content/uploads' \
--dry-run --skip-columns=guid --allow-root
# 确认无误后执行(--skip-columns=guid 避免动 guid 字段)
wp search-replace 'https://mimangfei.com/wp-content/uploads' \
'https://img.mimangfei.com/wp-content/uploads' \
--skip-columns=guid --allow-root
如果主题里硬编码了路径,也要一起找:
grep -rn "wp-content/uploads" /var/www/mimangfei/wp-content/themes/ | head -20
第六步:绑定自定义域名 + CDN
在 OSS 控制台绑定 img.mimangfei.com,配置 CNAME 到 OSS 提供的域名,再开 CDN 加速并申请 SSL 证书。开启后:
- 设置缓存策略:图片 30 天;
- 开启 WebP 自动转换(阿里云/腾讯云都支持);
- 开启防盗链:Referer 白名单加上你的域名。
常见踩坑
- 上传成功但前台 403:Bucket 权限设成了私有。对象存储默认是私有读写,必须手动改成公共读,或通过 CDN + 回源鉴权。
- 上传超时 / 失败:服务器到 OSS 走的公网,带宽小。同地域一定要开内网上传,速度从几 MB/s 变成几十 MB/s,且免流量费。
- 缩略图缺失:迁移时只传了原图,元数据里的 sizes 引用的
-300x200.jpg没传。必须整个目录同步,不能只挑大图。 - 案例:某站迁移后所有图片裂了,原因是替换 URL 时把
guid字段也改了,导致 WordPress 找不到附件。正确做法是--skip-columns=guid,或者在替换后跑一次wp media regenerate。 - AccessKey 泄露:不要把 AK 写进主题的
functions.php提交到 Git。用插件存数据库,或放wp-config.php的常量:
php
define( 'OSS_ACCESS_KEY_ID', 'LTAI****' );
define( 'OSS_ACCESS_KEY_SECRET', '****' );
- 删除本地文件后插件卸载 = 图片全丢:这是最惨的事故。保留本地副本至少一个月,或者至少保留一份完整备份。
- 费用陷阱:对象存储本身便宜,但外网下载流量费不便宜。一定要配 CDN,CDN 流量费通常比 OSS 直出便宜,且快很多。
总结
媒体库上云的正确顺序:建桶 → 装插件 → 命令行全量同步 → 验证数量 → 替换数据库 URL → 绑域名 + CDN → 观察一周 → 再考虑删本地。其中最危险的一步是删本地文件,务必在有完整备份的前提下做。上云之后,服务器镜像变小、备份变快、迁移变轻松,还能顺手开 WebP 和图片处理。下一步可以研究WordPress 图片优化与 WebP 转换,把图片体积再压一半。
