4、对象存储触发器:S3/OSS事件触发函数处理文件上传
好,咱们进入第四个核心话题。对象存储触发器,说白了就是「文件一上传,函数自动跑」。这个模式在 Serverless 架构里太常见了,我几乎每个项目都会用到。
你想想看,传统的做法是什么?写个轮询脚本,每隔几秒去检查有没有新文件。浪费资源不说,延迟还高。有了触发器,文件一落地,函数立刻被唤醒,这才是云原生的玩法。
4.1 触发器的工作原理
对象存储(比如 AWS S3、阿里云 OSS)本身是一个事件源。当你对存储桶里的对象做某些操作时——上传、删除、修改——它会发出一个事件通知。
这个通知会发给谁?在 Serverless 的世界里,就是发给你的函数。函数收到事件后,解析出文件信息,然后开始处理。
我画个简单的流程:
用户上传文件 → OSS/S3 产生事件 → 触发器捕获事件 → 调用函数 → 函数处理文件
整个过程是异步的,用户上传完文件就可以干别的事去了。函数在后台默默处理,处理完了你可以再通知用户。
核心要点:触发器是事件驱动的入口,它把「文件到达」和「业务处理」解耦了。
4.2 支持的触发事件类型
不同的云厂商支持的事件类型略有差异,但核心逻辑差不多。我以 AWS S3 和阿里云 OSS 为例,给你列个对照表:
| 事件类型 | AWS S3 | 阿里云 OSS | 说明 |
|---|---|---|---|
| 对象创建 | s3:ObjectCreated:* | oss:ObjectCreated:* | 上传、复制、追加等操作 |
| 对象删除 | s3:ObjectRemoved:* | oss:ObjectRemoved:* | 删除文件或目录 |
| 对象覆盖 | s3:ObjectCreated:Put | oss:ObjectCreated:Put | 同名文件重新上传 |
| 对象标签变更 | s3:ObjectTagging:* | 不支持 | 修改对象标签 |
我个人习惯,如果只是处理文件上传,就监听 ObjectCreated:* 就够了。但要注意,如果你只想处理「新上传」的文件,不想处理「复制」或「追加」产生的文件,那就得精确到 Put 事件。
小技巧:在阿里云 OSS 中,事件前缀和后缀过滤非常有用。比如你只处理 /incoming/ 目录下的 .csv 文件,可以设置前缀为 incoming/,后缀为 .csv。这样能减少很多不必要的函数调用。
4.3 实战:用函数处理上传的图片
咱们来写一个真实的例子。假设用户上传图片到 OSS,函数自动生成缩略图,并存到另一个目录。
这是阿里云函数计算(FC)的代码,用 Python 写的:
import oss2
from PIL import Image
import io
import json
def handler(event, context):
# 解析事件
events = json.loads(event)
for evt in events:
bucket_name = evt['oss']['bucket']['name']
object_key = evt['oss']['object']['key']
# 只处理 images/ 目录下的文件
if not object_key.startswith('images/'):
continue
# 初始化 OSS 客户端
auth = oss2.Auth(context.credentials.access_key_id,
context.credentials.access_key_secret)
bucket = oss2.Bucket(auth, 'oss-cn-hangzhou.aliyuncs.com', bucket_name)
# 下载原图
img_data = bucket.get_object(object_key).read()
img = Image.open(io.BytesIO(img_data))
# 生成缩略图
img.thumbnail((200, 200))
# 上传缩略图
thumb_key = object_key.replace('images/', 'thumbnails/', 1)
buffer = io.BytesIO()
img.save(buffer, 'JPEG')
buffer.seek(0)
bucket.put_object(thumb_key, buffer)
print(f'Thumbnail created: {thumb_key}')
return 'OK'
这段代码我解释一下:
- 事件里包含了桶名和文件路径,直接解析就行
- 我加了个前缀判断,只处理
images/目录下的文件 - 缩略图存到
thumbnails/目录,保持文件名不变
注意:千万别在函数里处理自己生成的缩略图!否则会陷入死循环——缩略图上传又触发函数,函数又生成缩略图……我曾经有个同事就踩过这个坑,一晚上跑了上万次函数,账单直接爆了。
4.4 配置触发器的注意事项
配置触发器看起来简单,但有几个坑我得提醒你:
- 权限问题:函数必须有权限读取对象存储。在阿里云上,需要给函数角色绑定
AliyunOSSReadOnlyAccess策略。如果还要写文件,就得给AliyunOSSFullAccess。 - 事件去重:同一个文件可能触发多次事件(比如分片上传)。我建议在函数里做幂等处理,比如先检查目标文件是否存在。
- 超时设置:处理大文件时,函数执行时间可能很长。记得把超时时间设大一点,我一般设 5 分钟。
- 并发控制:如果同时上传大量文件,函数会被高频调用。这时候要考虑函数并发上限,别把下游系统打垮了。
嗯,这里还要说一个我自己的经验。有一次线上出了个问题,用户上传了一个 2GB 的视频文件,函数直接超时了。后来我改成了「先触发函数做校验,再触发另一个函数做转码」的两阶段模式。小文件直接处理,大文件走异步队列。
4.5 事件结构解析
你可能会问:函数收到的 event 到底长什么样?我给你看个阿里云 OSS 事件的真实结构:
{
"events": [
{
"eventName": "ObjectCreated:Put",
"eventSource": "acs:oss",
"eventTime": "2024-01-15T10:00:00.000Z",
"eventVersion": "1.0",
"oss": {
"bucket": {
"name": "my-bucket",
"arn": "acs:oss:cn-hangzhou:123456:my-bucket"
},
"object": {
"key": "images/photo.jpg",
"size": 1024000,
"etag": "D41D8CD98F00B204E9800998ECF8427E",
"type": "jpg"
}
}
}
]
}
关键字段就三个:bucket.name、object.key、object.size。拿到这些信息,你就能定位到具体文件了。
调试技巧:刚开始配置触发器时,我建议先写一个简单的函数,只打印 event 内容。上传一个测试文件,看看日志里的事件结构对不对。确认无误后再写业务逻辑。
4.6 总结与最佳实践
对象存储触发器,说白了就是「事件驱动 + 无服务器」的经典组合。我总结几个最佳实践:
- 用前缀和后缀过滤事件,减少无效调用
- 函数要幂等,同一个文件触发多次也不怕
- 大文件走异步处理,别在函数里硬扛
- 监控函数调用次数和错误率,及时发现异常
下一章咱们会聊到「消息队列触发器」,那是处理海量数据的另一个利器。到时候你会发现,对象存储触发器和消息队列触发器经常搭配使用,一个负责入口,一个负责缓冲。
好,这一章就到这儿。有什么问题,欢迎在课程群里交流。