用 OBS + 函数工作流给静态博客图片自动压缩:一次低成本上云实践
【摘要】 一、背景与痛点我这个博客目前图片全丢在 OBS 桶里,CDN 回源拉取。前期没管体积,一张截图 2–3 MB 很常见,问题慢慢显出来:列表页一次加载十几张图,首屏明显卡OBS 存储量涨得快,虽然单价低但没必要白囤手动跑 tinypng 或 sharp 很烦,经常忘了压需求其实很明确:图片 PUT 到 OBS 后自动出一张压缩版,前端按后缀取图。二、方案设计没必要上 ECS 常驻进程,用事件触...
一、背景与痛点
我这个博客目前图片全丢在 OBS 桶里,CDN 回源拉取。前期没管体积,一张截图 2–3 MB 很常见,问题慢慢显出来:
- 列表页一次加载十几张图,首屏明显卡
- OBS 存储量涨得快,虽然单价低但没必要白囤
- 手动跑
tinypng或sharp很烦,经常忘了压
需求其实很明确:图片 PUT 到 OBS 后自动出一张压缩版,前端按后缀取图。
二、方案设计
没必要上 ECS 常驻进程,用事件触发最省事:
用户上传原图
↓
OBS PutObject 事件
↓
函数工作流 FunctionGraph 触发
↓
下载原图 → sharp 压缩 → 回写 OBS(加 -mini 后缀)
选型理由:
- OBS:存原图 + 压缩图,自带事件通知
- FunctionGraph:按调用计费,没流量不花钱,不用维护服务器
- Node.js + sharp:在函数环境里冷启动可接受,压缩质量稳
避坑点:函数写回 OBS 时别再触发同一事件,否则会死循环。用「压缩图走独立前缀 / 独立桶」或判断 key 是否包含-mini来解决。
三、实操步骤
3.1 创建 OBS 桶
建一个标准桶,比如
blog-assets-xxx,区域选离你用户近的(如华北-北京四)。开「事件通知」:
- 事件类型:
PutObject - 触发服务:函数工作流
- 触发函数:后面建完回填
3.2 创建函数工作流
运行时选 Node.js 18,内存 256MB,超时 30s 够用。
入口文件
index.js:const OBS = require('obs-nodejs-sdk');
const sharp = require('sharp');
const obsClient = new OBS({
access_key_id: process.env.AK,
secret_access_key: process.env.SK,
server: process.env.OBS_ENDPOINT,
});
const BUCKET = process.env.BUCKET;
exports.handler = async (event) => {
const records = event.Records || [];
for (const r of records) {
const key = r.s3.object.key;
if (key.includes('-mini.webp')) continue; // 防止回写再触发
// 1. 读原图
const data = await obsClient.getObject({
Bucket: BUCKET,
Key: key,
});
const body = Buffer.from(data.Body);
// 2. 压缩
const webp = await sharp(body)
.webp({ quality: 70 })
.resize({ width: 1600, withoutEnlargement: true })
.toBuffer();
// 3. 写回
const miniKey = key.replace(/(\.\w+)$/, '-mini.webp');
await obsClient.putObject({
Bucket: BUCKET,
Key: miniKey,
Body: webp,
ContentType: 'image/webp',
});
console.log(`compressed: ${key} -> ${miniKey}`);
}
return { status: 'ok' };
};
package.json 依赖:{
"dependencies": {
"obs-nodejs-sdk": "^3.1.0",
"sharp": "^0.33.0"
}
}
注:sharp在函数环境里建议用官方提供的兼容层或依赖包上传,别直接本地 mac 打 zip 上去,linux 二进制会对不上。
3.3 配置环境变量
函数配置里加:
AK/SK:用子账号密钥,只给该桶权限OBS_ENDPOINT:如obs.cn-north-4.myhuaweicloud.comBUCKET:桶名
IAM 权限最小集:
{
"Effect": "Allow",
"Action": ["obs:object:GetObject", "obs:object:PutObject"],
"Resource": ["obs:*:*:object:blog-assets-xxx/*"]
}
3.4 绑定 OBS 事件
回到 OBS 桶事件通知,把函数 URN 填进去。测试时手动传一张图:
aws s3 cp ./test.png s3://blog-assets-xxx/2026/test.png \
--endpoint-url https://obs.cn-north-4.myhuaweicloud.com
(或用 OBS Browser+、控制台上传都行)
四、验证结果
传一张 2.4 MB 的 PNG 截图:
- 原图:
2026/test.png - 自动生成:
2026/test-mini.webp - 体积降到 200–300 KB
- 函数日志里能看到
compressed: ...
前端改造也很简单,Markdown 里原图写原路径,列表页用
-mini.webp 后缀即可;或者用 CDN 回源规则按 UA 切换。实际压缩比看原图内容,截图/插画压得狠,照片类建议 quality 调到 75–80。
五、总结与可扩展
这套方案落地成本很低:
- 没开 ECS,纯 Serverless
- 日常博客量级每月函数调用费基本可忽略
- OBS 存储分层后续可把原图转低频访问
再往下可以加:
- 缩略图多尺寸(width=400/800/1600)
- AVIF 输出(sharp 支持,但函数内存可加到 512MB)
- 异常告警:函数报错推 SMN 邮件
- 原图自动转存归档桶
对个人站、文档站、内网知识库都通用,核心逻辑就是「OBS 事件 → 函数处理 → 回写」,换图片处理、音视频转码、CSV 清洗都一样套。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)