本文以新对象存入OSS Bucket触发函数执行为例,介绍如何在函数计算控制台为目标函数配置OSS触发器,包括创建触发器、配置入口参数以及编写函数代码并测试。

示例场景

您可以配置一个OSS触发器,在触发规则中设置前缀为source。当有图片存入指定的OSS Bucket中的source目录下,会自动触发函数执行,函数将图片缩放并存放到同一个Bucket的processed目录下。即source/serverless.png会被处理成processed/serverless.png

前提条件

步骤一:创建OSS触发器

  1. 登录函数计算控制台
  2. 在左侧导航栏,单击服务及函数
  3. 在顶部菜单栏,选择地域。
  4. 服务列表页面,找到目标服务,在其右侧操作列单击函数管理
  5. 函数管理页面,单击目标函数名称。
  6. 在函数详情页面,单击触发器管理页签,从版本或别名下拉列表选择要创建触发器的版本或别名,然后单击创建触发器
  7. 在创建触发器面板,填写相关信息。然后单击确定
    参数 操作 本文示例
    触发器类型 选择对象存储 OSS 对象存储 OSS
    名称 填写自定义的触发器名称。 oss-trigger
    版本或别名 默认值为LATEST,如果您需要创建其他版本或别名的触发器,需先在函数详情页的版本或别名下拉列表选择该版本。关于版本和别名的简介,请参见管理版本管理别名 LATEST
    Bucket 名称 选择已创建的OSS Bucket。 bucket-zh****
    文件前缀 输入要匹配的文件名称的前缀。强烈建议您配置前缀和后缀,避免触发事件嵌套循环触发引起额外费用。另外,一个Bucket的不同触发器如果指定了相同的事件类型,则前缀和后缀不能重复。详细信息,请参见触发规则
    注意 文件前缀不能以/开头。
    source
    文件后缀 输入要匹配的文件名称的后缀。强烈建议您配置前缀和后缀,避免触发事件嵌套循环触发引起额外费用。另外,一个Bucket的不同触发器如果指定了相同的事件类型,则前缀和后缀不能重复。详细信息,请参见触发规则 zip

    触发事件

    选择一个或多个触发事件。关于对象存储OSS的事件类型,请参见OSS事件定义

    本示例选择oss:ObjectCreated:PutObject

    oss:ObjectCreated:PutObject
    角色名称 选择AliyunOSSEventNotificationRole
    说明 如果您第一次创建该类型的触发器,则需要在单击确定后,在弹出的对话框中选择立即授权
    AliyunOSSEventNotificationRole

    创建完成后,在触发器名称列表中显示已创建的触发器。如需对创建的触发器进行修改或删除,具体操作,请参见触发器管理

步骤二:配置函数入口参数

OSS事件源会以event的形式作为输入参数传递给函数,您可以手动将event传给函数模拟触发事件。

  1. 在函数详情页面,单击函数代码页签,然后单击xialatubiao图标,从下拉列表中,选择配置测试参数
  2. 配置测试参数面板,选择创建新测试事件编辑已有测试事件页签,填写事件名称和事件内容。然后单击确定

    event是函数计算的入口参数,当指定的OSS Bucket发生对象创建或对象删除事件时,会将事件数据以JSON格式发送给绑定的函数。具体格式如下所示。

    {
      "events": [
        {
          "eventName": "ObjectCreated:PutObject",
          "eventSource": "acs:oss",
          "eventTime": "2021-08-13T06:45:43.000Z",
          "eventVersion": "1.0",
          "oss": {
            "bucket": {
              "arn": "acs:oss:cn-shanghai:123456789:bucketname",
              "name": "testbucket",
              "ownerIdentity": "123456789"
            },
            "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": "123456789"
          }
        }
      ]
    }           
    event参数中不同属性字段的解释如下表所示。
    参数 类型 示例值 描述
    eventName String ObjectCreated:PutObject 事件类型。
    eventSource String acs:oss 事件源,固定为acs:oss
    eventTime String 2021-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 123456789 创建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 123456789 请求发起者的阿里云账号ID。

步骤三:编写函数代码并测试

完成创建OSS触发器后,您可以开始编写函数代码并测试,以验证代码的正确性。在实际操作过程中发生OSS事件时,会自动触发函数执行。

警告 代码中一定要避免循环触发。一个典型的循环触发场景是OSS的某个Bucket上传文件事件触发函数执行,此函数执行完成后又生成了一个或多个文件再写回到OSS的Bucket里,这个写入动作又触发了函数执行,形成了链状循环。更多信息,请参见触发规则
  1. 在函数详情页面,单击函数代码页签,在代码编辑器中编写代码,然后单击保存并部署

    执行代码前的准备工作和代码示例如下:

    说明 如果您要在您的函数中读写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];
         // Required by OSS sdk: OSS region is prefixed with "oss-", e.g. "oss-cn-shanghai"
         var ossRegion = "oss-" + ossEvent.region;
         // Create oss client.
         var client = new oss({
             region: ossRegion,
             // Credentials can be retrieved from context
             accessKeyId: ctx.credentials.accessKeyId,
             accessKeySecret: ctx.credentials.accessKeySecret,
             stsToken: ctx.credentials.securityToken
         });
         // Bucket name is from OSS event
         client.useBucket(ossEvent.oss.bucket.name);
         // Processed images will be saved to processed/
         var newKey = ossEvent.oss.object.key.replace("source/", "processed/");
         var tmpFile = "/tmp/processed.png";
         // Get object
         console.log('Getting object: ', ossEvent.oss.object.key)
         client.get(ossEvent.oss.object.key).then(function (val) {
             // Read object from buffer
             jimp.read(val.content, function (err, image) {
                 if (err) {
                     console.error("Failed to read image");
                     callback(err);
                     return;
                 }
                 // Resize the image and save it to a tmp file
                 image.resize(128, 128).write(tmpFile, function (err) {
                     if (err) {
                         console.error("Failed to write image locally");
                         callback(err);
                         return;
                     }
                     // Putting object back to OSS with the new key
                     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());
            }
        }
    } 
  2. 单击函数代码页签的测试函数
    执行完成后,您可以在函数代码页签的上方查看执行结果。

更多信息

除了函数计算控制台,您还可通过以下方式配置触发器:
  • 通过Serverless Devs工具配置触发器。更多操作,请参见Serverless Devs
  • 通过SDK配置触发器。更多操作,请参见SDK列表

如需对创建的触发器进行修改或删除,具体操作,请参见触发器管理