用 OBS + 函数工作流给静态博客图片自动压缩:一次低成本上云实践

举报
yd_280727371 发表于 2026/10/21 10:51:39 2026/10/21
【摘要】 一、背景与痛点我这个博客目前图片全丢在 OBS 桶里,CDN 回源拉取。前期没管体积,一张截图 2–3 MB 很常见,问题慢慢显出来:列表页一次加载十几张图,首屏明显卡OBS 存储量涨得快,虽然单价低但没必要白囤手动跑 tinypng 或 sharp 很烦,经常忘了压需求其实很明确:图片 PUT 到 OBS 后自动出一张压缩版,前端按后缀取图。二、方案设计没必要上 ECS 常驻进程,用事件触...

一、背景与痛点

我这个博客目前图片全丢在 OBS 桶里,CDN 回源拉取。前期没管体积,一张截图 2–3 MB 很常见,问题慢慢显出来:
  1. 列表页一次加载十几张图,首屏明显卡
  2. OBS 存储量涨得快,虽然单价低但没必要白囤
  3. 手动跑 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.com
  • BUCKET:桶名
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)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。