以新对象存入OSS Bucket触发函数执行为例,本文介绍如何在函数计算控制台创建OSS触发器,包括创建触发器、配置函数入口参数以及编写函数代码并测试。
示例场景
您可以配置一个OSS触发器,并将其文件前缀设置为source
。当有图片存入指定的OSS Bucket中的source
目录下,会自动触发函数执行。函数将图片缩放并存放到同一个Bucket的processed
目录下,即将source/serverless.png
处理为processed/serverless.png
。
前提条件
步骤一:创建OSS触发器
- 登录函数计算控制台,在左侧导航栏,单击服务及函数。
- 在顶部菜单栏,选择地域,然后在服务列表页面,单击目标服务。
- 在函数管理页面,单击目标函数名称。
- 在函数详情页面,单击触发器管理页签,从版本或别名下拉列表选择要创建触发器的版本或别名,然后单击创建触发器。
- 在创建触发器面板,填写相关信息。然后单击确定。
配置项 操作 本文示例 触发器类型 选择对象存储 OSS。 对象存储 OSS 名称 填写自定义的触发器名称。 oss-trigger 版本或别名 默认值为LATEST,如果您需要创建其他版本或别名的触发器,需先在函数详情页的版本或别名下拉列表选择该版本。关于版本和别名的简介,请参见管理版本和管理别名。 LATEST Bucket 名称 选择已创建的OSS Bucket。 bucket-zh**** 文件前缀 输入要匹配的文件名称的前缀。建议您配置文件前缀和后缀,避免触发事件嵌套循环触发引起额外费用。此外,一个Bucket的不同触发器如果指定了相同的事件类型,则前缀和后缀不能重复。详细信息,请参见原生OSS触发器触发规则。 重要 文件前缀不能以/
开头,否则会导致OSS触发器无法被触发。source 文件后缀 输入要匹配的文件名称的后缀。强烈建议您配置前缀和后缀,避免触发事件嵌套循环触发引起额外费用。另外,一个Bucket的不同触发器如果指定了相同的事件类型,则前缀和后缀不能重复。详细信息,请参见原生OSS触发器触发规则。 zip 触发事件 选择一个或多个触发事件。关于对象存储OSS的事件类型,请参见OSS事件定义。 本示例选择oss:ObjectCreated:PutObject。
oss:ObjectCreated:PutObject 角色名称 选择AliyunOSSEventNotificationRole。 说明 如果您第一次创建该类型的触发器,则需要在单击确定后,在弹出的对话框中选择立即授权。AliyunOSSEventNotificationRole 创建完成后,在触发器名称列表中显示已创建的触发器。如需对创建的触发器进行修改或删除,具体操作,请参见触发器管理。
步骤二:配置函数入口参数
OSS事件源会以event的形式作为输入参数传递给函数,您可以手动将event传给函数模拟触发事件。
- 在函数详情页面,单击函数代码页签,然后单击
图标,从下拉列表中,选择配置测试参数。
- 在配置测试参数面板,选择创建新测试事件或编辑已有测试事件页签,填写事件名称和事件内容。然后单击确定。
event是函数计算的入口参数,当指定的OSS Bucket发生对象创建或对象删除事件时,会将事件数据以JSON格式发送给绑定的函数。具体格式如下所示。
{ "events":[ { "eventName":"ObjectCreated:PutObject", "eventSource":"acs:oss", "eventTime":"2022-08-13T06:45:43.000Z", "eventVersion":"1.0", "oss":{ "bucket":{ "arn":"acs:oss:cn-shanghai:123456789:bucketname", "name":"testbucket", "ownerIdentity":"164901546557****" }, "object":{ "deltaSize":122539, "eTag":"688A7BF4F233DC9C88A80BF985AB****", "key":"image/a.jpg", "size":122539 }, "ossSchemaVersion":"1.0", "ruleId":"9adac8e253828f4f7c0466d941fa3db81161****" }, "region":"cn-shanghai", "requestParameters":{ "sourceIPAddress":"140.205.XX.XX" }, "responseElements":{ "requestId":"58F9FF2D3DF792092E12044C" }, "userIdentity":{ "principalId":"164901546557****" } } ] }
event参数中不同属性字段的解释如下表所示。参数 类型 示例值 描述 eventName String ObjectCreated:PutObject 事件类型。 eventSource String acs:oss 事件源,固定为 acs:oss
。eventTime String 2022-08-13T06:45:43.000Z 事件产生的时间。使用ISO-8601标准时间格式。 eventVersion String 1.0 事件协议的版本。 oss Map OSS事件内容。 bucket Map bucket参数内容。 name String testbucket Bucket的名称。 arn String acs:oss:cn-shanghai:123456789:bucketname Bucket的唯一标识符。 ownerIdentity String 164901546557**** 创建Bucket的用户ID。 object Map object参数内容。 size Int 122539 object的大小。单位:Byte。 deltaSize Int 122539 object的大小变化量。单位:Byte。 - 如果新增一个文件,此参数的值表示文件大小。
- 如果同名覆盖一个文件,此参数的值表示新文件与旧文件的大小差值。
eTag String 688A7BF4F233DC9C88A80BF985AB**** Object的标签。 key String image/a.jpg Object的名称。 ossSchemaVersion String 1.0 OSS模式的版本号。 ruleId String 9adac8e253828f4f7c0466d941fa3db81161**** 事件匹配的规则ID。 region String cn-shanghai Bucket所在的地域。 requestParameters Map 请求参数。 sourceIPAddress String 140.205.XX.XX 请求的源IP地址。 responseElements Map 响应元素。 requestId String 58F9FF2D3DF792092E12044C 请求对应的Request ID。 userIdentity Map 用户属性。 principalId String 164901546557**** 请求发起者的阿里云账号ID。
步骤三:编写函数代码并测试
OSS触发器创建完成后,您可以开始编写函数代码并测试,以验证代码的正确性。在实际操作过程中,发生OSS事件时,函数会自动被触发执行。
- 在函数详情页面,单击函数代码页签,在代码编辑器中编写代码,然后单击部署代码。
执行代码前的准备工作和代码示例如下。
说明 如果您要在您的函数中读写OSS资源,建议使用OSS内网服务地址进行访问,避免使用公网访问,产生公网费用。关于OSS内网服务地址的格式,请参见访问域名和数据中心。/*准备工作: 1.请先在终端中执行以下代码安装package.json文件和jimp图片处理模块。 a.在终端执行npm init安装package.json文件 b.在终端执行npm install jimp安装jimp图片处理模块 2.请确保函数的函数入口(handler)为index.handler。 */ 'use strict'; console.log('Loading function ...'); var oss = require('ali-oss').Wrapper; var fs = require('fs'); var jimp = require("jimp"); module.exports.handler = function (eventBuf, ctx, callback) { console.log('Received event:', eventBuf.toString()); var event = JSON.parse(eventBuf); var ossEvent = event.events[0]; // OSS地域以“oss-”为前缀,例如“oss-cn-shanghai” var ossRegion = "oss-" + ossEvent.region; // 创建OSS客户端 var client = new oss({ region: ossRegion, // 从上下文中获取凭证 accessKeyId: ctx.credentials.accessKeyId, accessKeySecret: ctx.credentials.accessKeySecret, stsToken: ctx.credentials.securityToken }); // 从事件中获取Bucket名称 client.useBucket(ossEvent.oss.bucket.name); // 处理后的图片被存储至processed/目录 var newKey = ossEvent.oss.object.key.replace("source/", "processed/"); var tmpFile = "/tmp/processed.png"; // 获取OSS文件对象 console.log('Getting object: ', ossEvent.oss.object.key) client.get(ossEvent.oss.object.key).then(function (val) { // 从缓存中读取OSS文件对象内容 jimp.read(val.content, function (err, image) { if (err) { console.error("Failed to read image"); callback(err); return; } // 调整图片大小,并将其保存至tmp文件中 image.resize(128, 128).write(tmpFile, function (err) { if (err) { console.error("Failed to write image locally"); callback(err); return; } // 将读取到的文件对象上传到OSS存储空间中并重新命名 console.log('Putting object: ', newKey); client.put(newKey, tmpFile).then(function (val) { console.log('Put object:', val); callback(null, val); return; }).catch(function (err) { console.error('Failed to put object: %j', err); callback(err); return }); }); }); }).catch(function (err) { console.error('Failed to get object: %j', err); callback(err); return }); };
#准备工作: #1.请确保函数所在服务配置的角色具有访问对象存储OSS的权限。您可以登录RAM控制台,为该角色添加访问对象存储OSS的权限。 #2.请确保函数的函数入口(handler)为index.handler。 # -*- coding: utf-8 -*- import oss2, json from wand.image import Image def handler(event, context): evt = json.loads(event) creds = context.credentials # Required by OSS sdk auth=oss2.StsAuth( creds.access_key_id, creds.access_key_secret, creds.security_token) evt = evt['events'][0] bucket_name = evt['oss']['bucket']['name'] endpoint = 'oss-' + evt['region'] + '-internal.aliyuncs.com' bucket = oss2.Bucket(auth, endpoint, bucket_name) objectName = evt['oss']['object']['key'] # Processed images will be saved to processed/ newKey = objectName.replace("source/", "processed/") remote_stream = bucket.get_object(objectName) if not remote_stream: return remote_stream = remote_stream.read() with Image(blob=remote_stream) as img: with img.clone() as i: i.resize(128, 128) new_blob = i.make_blob() bucket.put_object(newKey, new_blob)
/*准备工作: 1.请确保函数所在服务配置的角色具有访问对象存储OSS的权限。您可以登录RAM控制台,为该角色添加访问对象存储OSS的权限。 2.请确保函数的函数入口(handler)为index.handler。 */ <?php use OSS\OssClient; function handler($event, $context) { $event = json_decode($event, $assoc = true); $accessKeyId = $context["credentials"]["accessKeyId"]; $accessKeySecret = $context["credentials"]["accessKeySecret"]; $securityToken = $context["credentials"]["securityToken"]; $evt = $event['events'][0]; $bucketName = $evt['oss']['bucket']['name']; $endpoint = 'oss-' . $evt['region'] . '-internal.aliyuncs.com'; $objectName = $evt['oss']['object']['key']; $newKey = str_replace("source/", "processed/", $objectName); try { $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false, $securityToken); $content = $ossClient->getObject($bucketName , $objectName); if ($content == null || $content == "") { return; } $imagick = new Imagick(); $imagick->readImageBlob($content); $imagick->resizeImage(128, 128, Imagick::FILTER_LANCZOS, 1); $ossClient->putObject($bucketName, $newKey, $imagick->getImageBlob()); } catch (OssException $e) { print($e->getMessage()); } }
/*准备工作: 1.请先增加如下依赖到pom.xml。 <dependencies> <dependency> <groupId>com.aliyun.fc.runtime</groupId> <artifactId>fc-java-core</artifactId> <version>1.4.1</version> </dependency> <dependency> <groupId>com.aliyun.fc.runtime</groupId> <artifactId>fc-java-event</artifactId> <version>1.2.0</version> </dependency> </dependencies> 2.请确保函数的函数入口(handler)为example.App::handleRequest。 */ package example; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import com.aliyun.fc.runtime.Context; import com.aliyun.fc.runtime.StreamRequestHandler; import com.aliyun.fc.runtime.event.OSSEvent; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; public class App implements StreamRequestHandler { private static final ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); @Override public void handleRequest( InputStream inputStream, OutputStream outputStream, Context context) throws IOException { OSSEvent ossEvent = mapper.readValue(inputStream, new TypeReference<OSSEvent>() {}); for (OSSEvent.Event event : ossEvent.events) { outputStream.write(String.format("received %s from %s @ %s", event.eventName, event.eventSource, event.region).getBytes()); outputStream.write(String.format("received bucket %s", event.oss.bucket.arn).getBytes()); outputStream.write(String.format("received object %s and it's size is %s", event.oss.object.key, event.oss.object.size).getBytes()); } } }
- 单击函数代码页签的测试函数。执行完成后,您可以在函数代码页签的上方查看执行结果。
常见问题
Q:如果上传文件到OSS只想触发函数执行一次,函数触发器的触发事件应该如何设置?
A:您可以设置触发器的触发事件为oss:ObjectCreated:PutObject
、oss:ObjectCreated:PostObject
和oss:ObjectCreated:CompleteMultipartUpload
。
如果调用分片上传,该过程会依次触发oss:ObjectCreated:InitiateMultipartUpload
、oss:ObjectCreated:UploadPart
和oss:ObjectCreated:CompleteMultipartUpload
事件。例如,您将函数触发器的触发事件设置为oss:ObjectCreated:*
,通过OSS Browser工具上传一个12 MB的文件,分片大小设置为5 MB,那么您的函数将被触发执行5次。5次对应的事件分别为oss:ObjectCreated:InitiateMultipartUpload
、oss:ObjectCreated:UploadPart
、oss:ObjectCreated:UploadPart
、oss:ObjectCreated:UploadPart
和oss:ObjectCreated:CompleteMultipartUpload
。
更多关于OSS触发器的触发规则,请参见触发器不能正常触发函数执行怎么办?。
相关操作
- 通过Serverless Devs工具配置触发器。更多操作,请参见Serverless Devs。
- 通过SDK配置触发器。更多操作,请参见SDK列表。
如需对创建的触发器进行修改或删除,具体操作,请参见触发器管理。