教程:使用 Amazon S3 触发器调用 Lambda 函数 - Amazon Lambda
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

教程:使用 Amazon S3 触发器调用 Lambda 函数

在本教程中,您将使用控制台创建 Lambda 函数,然后为 Amazon Simple Storage Service(Amazon S3)存储桶配置触发器。每次向 Amazon S3 存储桶添加对象时,您的函数都会运行并将该对象类型输出到 Amazon CloudWatch Logs。


      该图显示了 Amazon S3 存储桶、Lambda 函数和亚马逊日志之间的数据流  CloudWatch

您可以使用带有 Amazon S3 触发器的 Lambda 函数来执行多种类型的文件处理任务。例如,每当图像文件上传到 Amazon S3 存储桶时,您都可以使用 Lambda 函数创建缩略图,或者将上传的文档转换为不同的格式。完成本教程后,您可以尝试使用 Amazon S3 触发器创建缩略图教程来执行图像处理任务。

要完成本教程,请执行以下步骤:

  1. 创建 Amazon S3 存储桶。

  2. 创建一个 Lambda 函数,该函数会在 Amazon S3 存储桶中返回对象的类型。

  3. 配置一个 Lambda 触发器,该触发器将在对象上传到存储桶时调用函数。

  4. 先后使用虚拟事件和触发器测试函数。

完成这些步骤后,您将了解如何配置 Lambda 函数,使其在向 Amazon S3 存储桶添加或删除对象时运行。您仅可以使用 Amazon Web Services Management Console 完成此教程。

先决条件

如果您还没有 Amazon Web Services 账户,请完成以下步骤来创建一个。

注册 Amazon Web Services 账户
  1. 打开 https://portal.aws.amazon.com/billing/signup

  2. 按照屏幕上的说明进行操作。

    在注册时,将接到一通电话,要求使用电话键盘输入一个验证码。

    当您注册 Amazon Web Services 账户时,系统将会创建一个 Amazon Web Services 账户根用户。根用户有权访问该账户中的所有 Amazon Web Services 和资源。作为安全最佳实践,请为管理用户分配管理访问权限,并且只使用根用户执行 需要根用户访问权限的任务

注册过程完成后,Amazon 会向您发送一封确认电子邮件。在任何时候,您都可以通过转至 https://aws.amazon.com/ 并选择我的账户来查看当前的账户活动并管理您的账户。

注册 Amazon Web Services 账户 后,启用多重身份验证 (MFA) 保护您的管理用户。有关说明,请参阅《IAM 用户指南》中的为为 IAM 用户(控制台)启用虚拟 MFA 设备

要授予其他用户访问您的 Amazon Web Services 账户资源的权限,请创建 IAM 用户。为了保护您的 IAM 用户,请启用 MFA 并仅向 IAM 用户授予执行任务所需的权限。

有关创建和保护 IAM 用户的更多信息,请参阅《IAM 用户指南》中的以下主题:

创建 Amazon S3 存储桶


        显示您正在 Amazon S3 存储桶步骤中创建存储桶的教程工作流程图

首先使用 Amazon Web Services Management Console 创建 Amazon S3 存储桶。

创建 Amazon S3 存储桶
  1. 打开 Amazon S3 控制台并选择存储桶页面。

  2. 选择 Create bucket(创建存储桶)。

  3. General configuration(常规配置)下,执行以下操作:

    1. 对于存储桶名称,输入符合 Amazon S3 存储桶命名规则的全局唯一名称。存储桶名称只能由小写字母、数字、句点(.)和连字符(-)组成。

    2. 对于 Amazon Region(亚马逊云科技区域),选择一个区域。在本教程的后面部分,您必须在同个区域中创建 Lambda 函数。

  4. 将所有其他选项设置为默认值并选择创建存储桶

将测试对象上传到存储桶


        显示您正在 Amazon S3 存储桶步骤中上传测试对象的教程工作流程图

在本教程的后面部分,您将在 Lambda 控制台中测试 Lambda 函数。要确认函数代码能正常运行,Amazon S3 存储桶需要包含测试对象。此对象可以是您选择的任何文件(例如,HappyFace.jpg)。

要上传测试对象
  1. 打开 Amazon S3 控制台的存储桶页面,选择您在上一步中创建的存储桶。

  2. 选择上传

  3. 选择添加文件,然后使用文件选择器选择要上传的对象。

  4. 选择打开,然后选择上传

在本教程后面部分测试函数代码时,您会向其传递包含上传的对象的文件名的数据,因此不妨现在就记下来。

创建权限策略


        显示您正在 Lambda 函数步骤中创建权限策略的教程工作流程图

为 Lambda 函数创建执行角色之前,您首先要创建权限策略以授予函数访问所需 Amazon 资源的权限。在本教程中,该策略允许 Lambda 从 Amazon S3 存储桶获取对象并写入亚马逊 CloudWatch 日志。

创建策略
  1. 打开 IAM 控制台的 Policies(策略)页面

  2. 选择创建策略

  3. 选择 JSON 选项卡,然后将以下自定义策略粘贴到 JSON 编辑器中。

    { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:PutLogEvents", "logs:CreateLogGroup", "logs:CreateLogStream" ], "Resource": "arn:aws:logs:*:*:*" }, { "Effect": "Allow", "Action": [ "s3:GetObject" ], "Resource": "arn:aws:s3:::*/*" } ] }
  4. 选择下一步:标签

  5. 选择下一步:审核

  6. Review policy (查看策略) 下,为策略 Name (名称) 输入 s3-trigger-tutorial

  7. 选择创建策略

创建执行角色


        显示您正在 Lambda 函数步骤中创建执行角色的教程工作流程图

执行角色是一个 Amazon Identity and Access Management(IAM)角色,用于向 Lambda 函数授予访问 Amazon 服务和资源的权限。要使函数从 Amazon S3 存储桶获取对象,您需要附加上一步中创建的权限策略。

创建执行角色并附加自定义权限策略
  1. 打开 IAM 控制台的角色页面

  2. 选择创建角色

  3. 对于可信实体,选择 Amazon 服务,对于使用案例,选择 Lambda

  4. 选择下一步

  5. 在策略搜索框中,输入 s3-trigger-tutorial

  6. 在搜索结果中,选择您创建的策略(s3-trigger-tutorial),然后选择 Next(下一步)。

  7. Role details(角色详细信息)下,为 Role name(角色名称)输入 lambda-s3-trigger-role,然后选择 Create role(创建角色)。

创建 Lambda 函数


        教程工作流程图显示您正在 Lambda 函数步骤中创建函数。

在本例中,您将使用 Node.js 16.x 运行时系统在控制台中创建 Lambda 函数。您在控制台中创建的函数包含一些基本的“Hello World”代码。在下一个步骤中,您会将其替换为函数代码,以便从 Amazon S3 存储桶获取对象。

创建 Lambda 函数
  1. 打开 Lamba 控制台的函数页面

  2. 确保您在创建 Amazon S3 存储桶所在的同一 Amazon Web Services 区域 内操作。您可以使用屏幕顶部的下拉列表更改区域。

    
            显示了 Lambda 控制台中的区域下拉菜单的图像
  3. 选择创建函数

  4. 选择从头开始编写

  5. Basic information(基本信息)中,执行以下操作:

    1. 对于函数名称,输入 s3-trigger-tutorial

    2. 对于运行时系统,选择 Node.js 16.x

    3. 对于架构,选择 x86_64

  6. 更改默认执行角色选项卡中,执行以下操作:

    1. 展开选项卡,然后选择使用现有角色

    2. 选择您之前创建的 lambda-s3-trigger-role

  7. 选择创建函数

部署函数代码


        显示您正在 Lambda 函数步骤中部署代码的教程工作流程图

Lambda 函数将检索已上传对象的密钥名称和来自该对象从 Amazon S3 收到的 event 参数的存储桶名称。然后,该函数使用 Amazon SDK for JavaScript 中的 HeadObject API 调用来获取已上传对象的类型。

本教程使用 Node.js 16.x 运行时系统,但我们还提供了适用于其他运行时系统的示例代码文件。您可以选择以下框中的选项卡,查看适用于您感兴趣的运行时系统的代码。您将部署的 JavaScript 代码是标有标签的选项卡中显示的第一个示例JavaScript

.NET
Amazon SDK for .NET
注意

还有更多相关信息 GitHub。在无服务器示例存储库中查找完整示例,并了解如何进行设置和运行。

使用 .NET 将 S3 事件与 Lambda 结合使用。

using System.Threading.Tasks; using Amazon.Lambda.Core; using Amazon.S3; using System; using Amazon.Lambda.S3Events; using System.Web; // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] namespace S3Integration { public class Function { private static AmazonS3Client _s3Client; public Function() : this(null) { } internal Function(AmazonS3Client s3Client) { _s3Client = s3Client ?? new AmazonS3Client(); } public async Task<string> Handler(S3Event evt, ILambdaContext context) { try { if (evt.Records.Count <= 0) { context.Logger.LogLine("Empty S3 Event received"); return string.Empty; } var bucket = evt.Records[0].S3.Bucket.Name; var key = HttpUtility.UrlDecode(evt.Records[0].S3.Object.Key); context.Logger.LogLine($"Request is for {bucket} and {key}"); var objectResult = await _s3Client.GetObjectAsync(bucket, key); context.Logger.LogLine($"Returning {objectResult.Key}"); return objectResult.Key; } catch (Exception e) { context.Logger.LogLine($"Error processing request - {e.Message}"); return string.Empty; } } } }
Go
适用于 Go V2 的 SDK
注意

还有更多相关信息 GitHub。在无服务器示例存储库中查找完整示例,并了解如何进行设置和运行。

使用 Go 将 S3 事件与 Lambda 结合使用。

package main import ( "context" "log" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" ) func handler(ctx context.Context, s3Event events.S3Event) error { sdkConfig, err := config.LoadDefaultConfig(ctx) if err != nil { log.Printf("failed to load default config: %s", err) return err } s3Client := s3.NewFromConfig(sdkConfig) for _, record := range s3Event.Records { bucket := record.S3.Bucket.Name key := record.S3.Object.URLDecodedKey headOutput, err := s3Client.HeadObject(ctx, &s3.HeadObjectInput{ Bucket: &bucket, Key: &key, }) if err != nil { log.Printf("error getting head of object %s/%s: %s", bucket, key, err) return err } log.Printf("successfully retrieved %s/%s of type %s", bucket, key, *headOutput.ContentType) } return nil } func main() { lambda.Start(handler) }
Java
SDK for Java 2.x
注意

还有更多相关信息 GitHub。在无服务器示例存储库中查找完整示例,并了解如何进行设置和运行。

使用 Java 将 S3 事件与 Lambda 结合使用。

package example; import software.amazon.awssdk.services.s3.model.HeadObjectRequest; import software.amazon.awssdk.services.s3.model.HeadObjectResponse; import software.amazon.awssdk.services.s3.S3Client; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.S3Event; import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.S3EventNotificationRecord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Handler implements RequestHandler<S3Event, String> { private static final Logger logger = LoggerFactory.getLogger(Handler.class); @Override public String handleRequest(S3Event s3event, Context context) { try { S3EventNotificationRecord record = s3event.getRecords().get(0); String srcBucket = record.getS3().getBucket().getName(); String srcKey = record.getS3().getObject().getUrlDecodedKey(); S3Client s3Client = S3Client.builder().build(); HeadObjectResponse headObject = getHeadObject(s3Client, srcBucket, srcKey); logger.info("Successfully retrieved " + srcBucket + "/" + srcKey + " of type " + headObject.contentType()); return "Ok"; } catch (Exception e) { throw new RuntimeException(e); } } private HeadObjectResponse getHeadObject(S3Client s3Client, String bucket, String key) { HeadObjectRequest headObjectRequest = HeadObjectRequest.builder() .bucket(bucket) .key(key) .build(); return s3Client.headObject(headObjectRequest); } }
JavaScript
适用于 JavaScript (v2) 的软件开发工具包
注意

还有更多相关信息 GitHub。在无服务器示例存储库中查找完整示例,并了解如何进行设置和运行。

使用 Lambda 使用 S3 事件。 JavaScript

const aws = require('aws-sdk'); const s3 = new aws.S3({ apiVersion: '2006-03-01' }); exports.handler = async (event, context) => { // Get the object from the event and show its content type const bucket = event.Records[0].s3.bucket.name; const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' ')); const params = { Bucket: bucket, Key: key, }; try { const { ContentType } = await s3.headObject(params).promise(); console.log('CONTENT TYPE:', ContentType); return ContentType; } catch (err) { console.log(err); const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`; console.log(message); throw new Error(message); } };

使用 Lambda 使用 S3 事件。 TypeScript

import { S3Event } from 'aws-lambda'; import { S3Client, HeadObjectCommand } from '@aws-sdk/client-s3'; const s3 = new S3Client({ region: process.env.AWS_REGION }); export const handler = async (event: S3Event): Promise<string | undefined> => { // Get the object from the event and show its content type const bucket = event.Records[0].s3.bucket.name; const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' ')); const params = { Bucket: bucket, Key: key, }; try { const { ContentType } = await s3.send(new HeadObjectCommand(params)); console.log('CONTENT TYPE:', ContentType); return ContentType; } catch (err) { console.log(err); const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`; console.log(message); throw new Error(message); } };
Python
SDK for Python (Boto3)
注意

还有更多相关信息 GitHub。在无服务器示例存储库中查找完整示例,并了解如何进行设置和运行。

使用 Python 将 S3 事件与 Lambda 结合使用。

import json import urllib.parse import boto3 print('Loading function') s3 = boto3.client('s3') def lambda_handler(event, context): #print("Received event: " + json.dumps(event, indent=2)) # Get the object from the event and show its content type bucket = event['Records'][0]['s3']['bucket']['name'] key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8') try: response = s3.get_object(Bucket=bucket, Key=key) print("CONTENT TYPE: " + response['ContentType']) return response['ContentType'] except Exception as e: print(e) print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket)) raise e
Rust
适用于 Rust 的 SDK
注意

还有更多相关信息 GitHub。在无服务器示例存储库中查找完整示例,并了解如何进行设置和运行。

使用 Rust 将 S3 事件与 Lambda 结合使用。

use aws_lambda_events::event::s3::S3Event; use aws_sdk_s3::{Client}; use lambda_runtime::{run, service_fn, Error, LambdaEvent}; /// Main function #[tokio::main] async fn main() -> Result<(), Error> { tracing_subscriber::fmt() .with_max_level(tracing::Level::INFO) .with_target(false) .without_time() .init(); // Initialize the AWS SDK for Rust let config = aws_config::load_from_env().await; let s3_client = Client::new(&config); let res = run(service_fn(|request: LambdaEvent<S3Event>| { function_handler(&s3_client, request) })).await; res } async fn function_handler( s3_client: &Client, evt: LambdaEvent<S3Event> ) -> Result<(), Error> { tracing::info!(records = ?evt.payload.records.len(), "Received request from SQS"); if evt.payload.records.len() == 0 { tracing::info!("Empty S3 event received"); } let bucket = evt.payload.records[0].s3.bucket.name.as_ref().expect("Bucket name to exist"); let key = evt.payload.records[0].s3.object.key.as_ref().expect("Object key to exist"); tracing::info!("Request is for {} and object {}", bucket, key); let s3_get_object_result = s3_client .get_object() .bucket(bucket) .key(key) .send() .await; match s3_get_object_result { Ok(_) => tracing::info!("S3 Get Object success, the s3GetObjectResult contains a 'body' property of type ByteStream"), Err(_) => tracing::info!("Failure with S3 Get Object request") } Ok(()) }
要部署函数代码
  1. 打开 Lamba 控制台的函数页面

  2. 选择您在上一步中创建的函数 (s3-trigger-tutorial)。

  3. 选择节点选项卡。

  4. 将提供的 JavaScript 代码复制并粘贴到代码源窗格的 index.js 选项卡中。

  5. 选择部署

创建 Amazon S3 触发器


        显示您正在 S3 触发器步骤中创建触发器的教程工作流程图

现在,您已部署函数代码,可以创建将调用函数的 Amazon S3 触发器了。

创建 Amazon S3 触发器
  1. 在函数控制台页面的函数概述窗格中,选择添加触发器

  2. 选择 S3

  3. 存储桶下,选择您在本教程前面步骤中创建的存储桶。

  4. 事件类型下,选择所有对象创建事件。您也可以配置触发器,以便在删除对象时调用 Lambda,但我们不会在本教程中使用该选项。

  5. 递归调用下,选中复选框以确认知晓不建议使用相同的 Amazon S3 存储桶用于输入和输出。您可以阅读 Serverless Land 中的 Recursive patterns that cause run-away Lambda functions,进一步了解 Lambda 中的递归调用模式。

  6. 选择添加

使用虚拟事件测试 Lambda 函数


        显示您正在测试步骤中使用虚拟事件进行测试的教程工作流程图

现在您已经创建并配置了 Lambda 函数,可以对其进行测试了。您首先通过向其发送 Amazon S3 虚拟事件来测试函数,以确认函数可以正常运行。

要使用虚拟事件测试 Lambda 函数
  1. 在函数的 Lambda 控制台页面中,选择代码选项卡。

  2. 代码源窗格中,选择测试

  3. 配置测试事件框中,执行以下操作:

    1. 对于事件名称,输入 MyTestEvent

    2. 对于模板,选择 S3 Put

    3. 在 Event JSON(事件 JSON)中,替换以下值:

      • 使用 us-east-1 替换要在其中创建 Amazon S3 存储桶的区域。

      • my-bucket 的两个实例都替换为 Amazon S3 存储桶的名称。

      • test%2FKey 替换为您之前上传到存储桶的测试对象的名称(例如,HappyFace.jpg)。

      { "Records": [ { "eventVersion": "2.0", "eventSource": "aws:s3", "awsRegion": "us-east-1", "eventTime": "1970-01-01T00:00:00.000Z", "eventName": "ObjectCreated:Put", "userIdentity": { "principalId": "EXAMPLE" }, "requestParameters": { "sourceIPAddress": "127.0.0.1" }, "responseElements": { "x-amz-request-id": "EXAMPLE123456789", "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH" }, "s3": { "s3SchemaVersion": "1.0", "configurationId": "testConfigRule", "bucket": { "name": "my-bucket", "ownerIdentity": { "principalId": "EXAMPLE" }, "arn": "arn:aws:s3:::my-bucket" }, "object": { "key": "test%2Fkey", "size": 1024, "eTag": "0123456789abcdef0123456789abcdef", "sequencer": "0A1B2C3D4E5F678901" } } } ] }
    4. 选择保存

  4. 代码源窗格中,选择测试

  5. 如果函数成功运行,您将在执行结果选项卡中看到如下输出。

    Response "image/jpeg" Function Logs START RequestId: 12b3cae7-5f4e-415e-93e6-416b8f8b66e6 Version: $LATEST 2021-02-18T21:40:59.280Z 12b3cae7-5f4e-415e-93e6-416b8f8b66e6 INFO INPUT BUCKET AND KEY: { Bucket: 'my-bucket', Key: 'HappyFace.jpg' } 2021-02-18T21:41:00.215Z 12b3cae7-5f4e-415e-93e6-416b8f8b66e6 INFO CONTENT TYPE: image/jpeg END RequestId: 12b3cae7-5f4e-415e-93e6-416b8f8b66e6 REPORT RequestId: 12b3cae7-5f4e-415e-93e6-416b8f8b66e6 Duration: 976.25 ms Billed Duration: 977 ms Memory Size: 128 MB Max Memory Used: 90 MB Init Duration: 430.47 ms Request ID 12b3cae7-5f4e-415e-93e6-416b8f8b66e6

使用 Amazon S3 触发器测试 Lambda 函数


          显示您正在测试步骤中使用 S3 触发器进行测试的教程工作流程图

要使用配置的触发器测试函数,您需要使用控制台将对象上传到 Amazon S3 存储桶。要验证您的 Lambda 函数是否已正确调用,请使用 CloudWatch 日志查看函数的输出。

要将对象上传到 Amazon S3 存储桶
  1. 打开 Amazon S3 控制台的存储桶页面,选择您之前创建的存储桶。

  2. 选择上传

  3. 选择添加文件,然后使用文件选择器选择要上传的对象。此对象可以是您选择的任何文件。

  4. 选择打开,然后选择上传

使用 CloudWatch 日志验证操作是否正确
  1. 打开 CloudWatch 控制台

  2. 确保您在创建 Lambda 函数所在相同的 Amazon Web Services 区域 操作。您可以使用屏幕顶部的下拉列表更改区域。

    
              显示了 Lambda 控制台中的区域下拉菜单的图像
  3. 选择日志,然后选择日志组

  4. 选择函数 (/aws/lambda/s3-trigger-tutorial) 的日志组。

  5. 日志流下,选择最新的日志流。

  6. 如果函数已正确调用以响应 Amazon S3 触发器,您将看到如下输出。您看到的 CONTENT TYPE 取决于上传到存储桶的文件类型。

    2022-05-09T23:17:28.702Z 0cae7f5a-b0af-4c73-8563-a3430333cc10 INFO CONTENT TYPE: image/jpeg

清除资源

除非您想要保留为本教程创建的资源,否则可立即将其删除。通过删除您不再使用的 Amazon 资源,可防止您的 Amazon 账户产生不必要的费用。

删除 Lambda 函数
  1. 打开 Lamba 控制台的 Functions(函数)页面

  2. 选择您创建的函数。

  3. 依次选择操作删除

  4. 在文本输入字段中键入 delete,然后选择删除

删除执行角色
  1. 打开 IAM 控制台的角色页面

  2. 选择您创建的执行角色。

  3. 选择删除

  4. 在文本输入字段中输入角色名称,然后选择 Delete(删除)。

删除 S3 存储桶
  1. 打开 Amazon S3 控制台

  2. 选择您创建的存储桶。

  3. 选择 Delete(删除)。

  4. 在文本输入字段中输入存储桶的名称。

  5. 选择删除存储桶

后续步骤

试用更高级的教程。在本教程中,Amazon S3 触发器调用函数来为上传到 S3 存储桶的每个图像文件创建缩略图。本教程需要适度的Amazon和 Lambda 领域知识水平。您可以使用 Amazon Command Line Interface (Amazon CLI) 创建资源,然后为函数及其依赖项创建 .zip 文件存档部署包。