用华为云 FunctionGraph 实现 OBS 图片自动压缩:零服务器的图片处理流水线

举报
yd_274671247 发表于 2026/11/14 15:37:46 2026/11/14
【摘要】 最近整理个人博客的图片时发现一个问题:手机直出的照片动不动就三四兆,文章里放上几张,页面加载明显变慢。手动一张张压缩再上传太费时间,而且容易忘记。想了一下,既然图片都存在华为云 OBS 里,能不能让图片一上传就自动压缩好?这篇文章记录我用华为云 FunctionGraph(函数工作流)搭的一条小流水线:图片上传到 OBS 源桶后自动触发函数,函数把图片压缩后写到目标桶,整个过程不需要维护任何...

最近整理个人博客的图片时发现一个问题:手机直出的照片动不动就三四兆,文章里放上几张,页面加载明显变慢。手动一张张压缩再上传太费时间,而且容易忘记。想了一下,既然图片都存在华为云 OBS 里,能不能让图片一上传就自动压缩好?

这篇文章记录我用华为云 FunctionGraph(函数工作流)搭的一条小流水线:图片上传到 OBS 源桶后自动触发函数,函数把图片压缩后写到目标桶,整个过程不需要维护任何服务器。流程跑通后,博客配图、商品图这类场景都能直接复用。

方案架构

核心思路是事件驱动:

  1. 用户或程序把原图上传到 OBS 的源桶,例如 images/original/ 目录;
  2. OBS 的对象创建事件触发 FunctionGraph 函数;
  3. 函数下载原图,用 Pillow 做等比缩放和有损压缩;
  4. 压缩后的图片写入目标桶,例如 images/compressed/,业务侧只读目标桶。

需要强调的一点是:源桶和目标桶要分开,至少前缀不能重叠。如果处理完又写回同一个目录,会再次触发函数,形成循环触发。这个坑我第一次就踩了,后面专门把两个桶拆开才解决。

准备工作

  • 两个 OBS 桶:源桶和目标桶,建议和函数选在同一个区域,内网传输不走公网,也更快;
  • FunctionGraph 服务已开通;
  • 给函数配置委托,让函数有权限读写这两个桶。控制台创建函数时,在权限配置里选择新建委托,勾选 OBS 的读写权限即可。

第一步:创建函数

在 FunctionGraph 控制台创建函数,运行时选择 Python 3.9,内存建议 512 MB,超时时间设为 60 秒。图片处理是 CPU 密集型活儿,内存给小了容易超时;单张图几兆的情况下,这个配置够用了。入口函数就叫 handler,后面的代码都围绕它写。

第二步:配置 OBS 触发器

函数创建好后,在触发器里添加 OBS 触发器:

  • 事件类型:ObjectCreated:Put,也就是对象创建后触发,上传完成即触发;
  • 桶:选择源桶;
  • 事件源前缀:images/original/;
  • 后缀:.jpg、.png,可以添加多条。

前缀加后缀的组合可以把触发范围收得很窄,临时文件、非图片文件不会误触发。

第三步:写处理代码

函数的逻辑分四步:解析事件、下载原图、压缩、上传结果。事件里会带上桶名和对象 Key,触发器控制台的测试事件里可以看到完整的事件示例,发布前先对照着核对字段。

import os
from obs import ObsClient
from PIL import Image

obs = ObsClient(
    access_key_id=os.environ['OBS_AK'],
    secret_access_key=os.environ['OBS_SK'],
    server='obs.cn-north-4.myhuaweicloud.com'
)

def handler(event, context):
    record = event['Records'][0]
    bucket = record['obs']['bucket']['name']
    key = record['obs']['object']['key']
    name = os.path.basename(key)
    local_path = '/tmp/' + name
    obs.getObject(bucket, key, downloadPath=local_path)
    img = Image.open(local_path)
    img.thumbnail((1600, 1600))  # 等比缩放,最长边不超过 1600
    out_path = '/tmp/compressed_' + name
    img.convert('RGB').save(out_path, 'JPEG', quality=80)
    obs.putFile('my-images-processed',
                'images/compressed/' + name, out_path)
    return 'ok'

几点说明:

  • AK、SK 放在函数的环境变量里,建议用最小权限的子账号 AK、SK,不要把主账号的密钥写进代码;
  • Pillow 需要随部署包一起上传,函数运行环境默认不带这个库。本地用 pip install -t . pillow 装好后,连同代码一起打成 zip 上传;也可以用 FunctionGraph 的自定义层来装依赖;
  • /tmp 是函数的临时磁盘空间,原图建议先控制在几十兆以内;特别大的文件,可以先在上传端限制体积。

第四步:测试验证

上传一张 3MB 左右的照片到源桶的 images/original/ 目录,几秒后去目标桶检查:

  • 目标桶出现同名压缩图,体积一般能压到几百 KB,具体看原图内容;
  • FunctionGraph 控制台的调用记录里出现一次成功调用;
  • 打开函数的日志,能看到下载、压缩、上传三步的打印。

如果没有触发,先检查触发器的前缀和后缀;如果调用失败,在日志里看具体报错,多数是权限或依赖问题。

计费与成本

FunctionGraph 按请求次数和执行时长计费,每月有一定的免费额度。个人博客这种低频场景,调用量很小,基本落在免费额度内。存储还是走 OBS 的费用,图片压缩后目标桶的对象更小,顺带把存储和 CDN 回源流量也省下来了。调用量如果突增,可以在函数上设置并发上限,并配上告警。

可以继续扩展的方向

  • 同一张图生成多个尺寸,比如封面图和缩略图,分别写回目标桶的不同前缀;
  • 压缩的同时加水印;
  • 把 JPEG 转成 WebP,进一步减小体积;
  • 处理失败时通过消息通知服务发短信或邮件告警。

小结

这条流水线搭下来,全程没有服务器,只有两个桶加一个函数。图片上传后几秒钟就能在目标桶里看到压缩结果,博客的图片加载速度也肉眼可见地快了。对于图片量不大的个人项目和小型站点,这种上传即处理的思路很划算:有变动时改一行代码重新发布就行,不用重新部署整套服务。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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