AWS Lambda
开发人员指南
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 AWS 服务入门

教程:将 AWS Lambda 与 Amazon S3 结合使用

假设您要为上传到存储桶的每个图像文件创建一个缩略图。您可以创建一个 Lambda 函数 (CreateThumbnail),在创建对象后,Amazon S3 可调用该函数。之后,Lambda 函数可以从源存储桶读取图像对象并创建缩略图目标存储桶。

完成本教程后,您的账户中将具有以下 Amazon S3、Lambda 和 IAM 资源:

Lambda 资源

  • Lambda 函数。

  • 一个与 Lambda 函数关联的访问策略,此策略向 Amazon S3 授予调用 Lambda 函数的权限。

IAM 资源

  • 一个执行角色,此角色通过与其关联的权限策略授予您的 Lambda 函数所需的权限。

Amazon S3 资源

  • 具有调用 Lambda 函数的通知配置的源存储桶。

  • 函数在其中保存已调整大小的图像的目标存储桶。

先决条件

本教程假设您对基本 Lambda 操作和 Lambda 控制台有一定了解。如果尚不了解,请按照开始使用 AWS Lambda中的说明创建您的第一个 Lambda 函数。

为了遵循本指南中的步骤,您需要命令行终端或外壳,以便运行命令。命令显示在列表中,以提示符 ($) 和当前目录名称(如果有)开头:

~/lambda-project$ this is a command this is output

对于长命令,使用转义字符 (\) 将命令拆分到多行中。

在 Linux 和 macOS 中,可使用您首选的外壳程序和程序包管理器。在 Windows 10 中,您可以 安装 Windows Subsystem for Linux,获取 Ubuntu 和 Bash 与 Windows 集成的版本。

安装 NPM 来管理函数的依赖关系。

创建执行角色

创建执行角色,向您的函数授予访问 AWS 资源的权限。

创建执行角色

  1. 打开 IAM 控制台中的“角色”页面

  2. 选择 Create role (创建角色)

  3. 创建具有以下属性的角色。

    • 可信任的实体AWS Lambda

    • 权限AWSLambdaExecute

    • 角色名称 (角色名称)lambda-s3-role

AWSLambdaExecute 策略具有该函数在 Amazon S3 中管理对象并将日志写入 CloudWatch Logs 所需的权限。

创建存储桶并上传示例对象

按照以下步骤创建存储桶并上传对象。

  1. 打开 Amazon S3 控制台

  2. 创建两个存储桶。目标存储桶名称必须为后跟 resizedsource,其中 source 是您希望用于源的存储桶的名称。例如,mybucketmybucketresized

  3. 在源存储桶中,上传一个 .jpg 对象 HappyFace.jpg

    在连接到 Amazon S3 之前手动调用 Lambda 函数时,您要将示例事件数据传递到指定源存储桶和 HappyFace.jpg 作为新建对象的函数,因此您需要先创建此示例对象。

创建函数

以下示例代码接收 Amazon S3 事件输入并对其所包含的消息进行处理。它调整源存储桶中图像的大小并将输出保存到目标存储桶。

注意

有关使用其他语言的示例代码,请参阅 示例 Amazon Simple Storage Service 函数代码

例 index.js

// dependencies var async = require('async'); var AWS = require('aws-sdk'); var gm = require('gm') .subClass({ imageMagick: true }); // Enable ImageMagick integration. var util = require('util'); // constants var MAX_WIDTH = 100; var MAX_HEIGHT = 100; // get reference to S3 client var s3 = new AWS.S3(); exports.handler = function(event, context, callback) { // Read options from the event. console.log("Reading options from event:\n", util.inspect(event, {depth: 5})); var srcBucket = event.Records[0].s3.bucket.name; // Object key may have spaces or unicode non-ASCII characters. var srcKey = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " ")); var dstBucket = srcBucket + "resized"; var dstKey = "resized-" + srcKey; // Sanity check: validate that source and destination are different buckets. if (srcBucket == dstBucket) { callback("Source and destination buckets are the same."); return; } // Infer the image type. var typeMatch = srcKey.match(/\.([^.]*)$/); if (!typeMatch) { callback("Could not determine the image type."); return; } var imageType = typeMatch[1].toLowerCase(); if (imageType != "jpg" && imageType != "png") { callback(`Unsupported image type: ${imageType}`); return; } // Download the image from S3, transform, and upload to a different S3 bucket. async.waterfall([ function download(next) { // Download the image from S3 into a buffer. s3.getObject({ Bucket: srcBucket, Key: srcKey }, next); }, function transform(response, next) { gm(response.Body).size(function(err, size) { // Infer the scaling factor to avoid stretching the image unnaturally. var scalingFactor = Math.min( MAX_WIDTH / size.width, MAX_HEIGHT / size.height ); var width = scalingFactor * size.width; var height = scalingFactor * size.height; // Transform the image buffer in memory. this.resize(width, height) .toBuffer(imageType, function(err, buffer) { if (err) { next(err); } else { next(null, response.ContentType, buffer); } }); }); }, function upload(contentType, data, next) { // Stream the transformed image to a different S3 bucket. s3.putObject({ Bucket: dstBucket, Key: dstKey, Body: data, ContentType: contentType }, next); } ], function (err) { if (err) { console.error( 'Unable to resize ' + srcBucket + '/' + srcKey + ' and upload to ' + dstBucket + '/' + dstKey + ' due to an error: ' + err ); } else { console.log( 'Successfully resized ' + srcBucket + '/' + srcKey + ' and uploaded to ' + dstBucket + '/' + dstKey ); } callback(null, "message"); } ); };

查看上述代码并注意以下内容:

  • 函数通过作为参数接收的事件数据获知源存储桶名称和对象键名称。如果对象为 .jpg,则该代码会创建一个缩略图并将其保存到目标存储桶。

  • 该代码假定目标存储桶已存在,且其名称为源存储桶名称后跟字符串 resized。例如,如果在事件数据中识别的源存储桶为 examplebucket,则代码假定您具有目标存储桶 examplebucketresized

  • 对于所创建的缩略图,该代码会将其键名称派生为后跟源对象键名称的字符串 resized-。例如,如果源对象键为 sample.jpg,则代码会创建具有键 resized-sample.jpg 的缩略图对象。

部署程序包是包含 Lambda 函数代码和依赖项的 .zip 文件。

创建部署程序包

  1. 将函数代码保存为名为 lambda-s3 的文件夹中的 index.js

  2. 使用 NPM 安装 GraphicsMagick 和 Async 库。

    lambda-s3$ npm install async gm

    完成此步骤后,文件夹结构如下:

    lambda-s3 |- index.js |- /node_modules/gm └ /node_modules/async
  3. 创建包含函数代码和依赖项的部署包。

    lambda-s3$ zip -r function.zip .

创建函数

  • 使用 create-function 命令创建 Lambda 函数。

    $ aws lambda create-function --function-name CreateThumbnail \ --zip-file fileb://function.zip --handler index.handler --runtime nodejs8.10 \ --timeout 10 --memory-size 1024 \ --role arn:aws:iam::123456789012:role/lambda-s3-role

之前的命令指定 10 秒超时值作为函数配置。根据上传的对象的大小,可能需要使用下面的 AWS CLI 命令增大超时值。

$ aws lambda update-function-configuration --function-name CreateThumbnail --timeout 30

测试 Lambda 函数。

在本步骤中,您将使用示例 Amazon S3 事件数据手动调用 Lambda 函数。

测试 Lambda 函数

  1. 将下面的 Amazon S3 示例事件数据保存到某个文件中,并将该文件另存为 inputFile.txt。您需要提供 sourcebucket 名称和 .jpg 对象键来更新该 JSON。

    { "Records":[ { "eventVersion":"2.0", "eventSource":"aws:s3", "awsRegion":"us-west-2", "eventTime":"1970-01-01T00:00:00.000Z", "eventName":"ObjectCreated:Put", "userIdentity":{ "principalId":"AIDAJDPLRKLG7UEXAMPLE" }, "requestParameters":{ "sourceIPAddress":"127.0.0.1" }, "responseElements":{ "x-amz-request-id":"C3D13FE58DE4C810", "x-amz-id-2":"FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD" }, "s3":{ "s3SchemaVersion":"1.0", "configurationId":"testConfigRule", "bucket":{ "name":"sourcebucket", "ownerIdentity":{ "principalId":"A3NL1KOZZKExample" }, "arn":"arn:aws:s3:::sourcebucket" }, "object":{ "key":"HappyFace.jpg", "size":1024, "eTag":"d41d8cd98f00b204e9800998ecf8427e", "versionId":"096fKKXTRTtl3on89fVO.nfljtsv6qko" } } } ] }
  2. 运行下面的 Lambda CLI invoke 命令以调用函数。请注意,该命令会请求异步执行。(可选)可通过将 RequestResponse 指定为 invocation-type 参数值来同步调用它。

    $ aws lambda invoke --function-name CreateThumbnail --invocation-type Event \ --payload file://inputfile.txt outputfile.txt
  3. 验证目标存储桶中是否已创建缩略图。

配置 Amazon S3 以发布事件

在本步骤中,您将添加剩余的配置,以便 Amazon S3 能够向 AWS Lambda 发布对象创建事件并调用 Lambda 函数。您将在本步骤中执行以下操作:

  • 向 Lambda 函数访问策略添加权限以允许 Amazon S3 调用该函数。

  • 向源存储桶添加通知配置。在通知配置中,您需要提供以下内容:

    • 需要 Amazon S3 发布的事件的事件类型。在本教程中,您将指定 s3:ObjectCreated:* 事件类型,以便 Amazon S3 在创建对象时发布事件。

    • 要调用的 Lambda 函数。

向函数策略添加权限

  1. 运行下面的 Lambda CLI add-permission 命令以向 Amazon S3 服务委托人 (s3.amazonaws.com) 授予执行 lambda:InvokeFunction 操作的权限。请注意,向 Amazon S3 授予权限,使其只能在满足以下条件时调用该函数:

    • 在特定的存储桶上检测到对象创建事件。

    • 存储桶归特定的 AWS 账户所有。如果存储桶拥有者删除了某个存储桶,则其他 AWS 账户可以创建使用该名称的存储桶。该条件确保只有特定的 AWS 账户能调用您的 Lambda 函数。

    $ aws lambda add-permission --function-name CreateThumbnail --principal s3.amazonaws.com \ --statement-id some-unique-id --action "lambda:InvokeFunction" \ --source-arn arn:aws:s3:::sourcebucket \ --source-account bucket-owner-account-id
  2. 通过运行 AWS CLI get-policy 命令验证函数的访问策略。

    $ aws lambda get-policy --function-name function-name

在源存储桶上添加通知配置,以请求 Amazon S3 向 Lambda 发布对象创建事件。

配置通知

  1. 打开 Amazon S3 控制台

  2. 选择源存储桶。

  3. 选择 Properties.

  4. Events (事件) 下,使用以下设置配置通知。

    • 名称lambda-trigger

    • 事件ObjectCreate (All)

    • 发送到Lambda function

    • LambdaCreateThumbnail

有关事件配置的更多信息,请参阅 Amazon Simple Storage Service 控制台用户指南 中的启用事件通知

测试设置

现在,可以按以下方式测试设置:

  1. 使用 Amazon S3 控制台将 .jpg 或 .png 对象上传到源存储桶。

  2. 使用 CreateThumbnail 函数验证是否在目标存储桶中创建了缩略图。

  3. 在 CloudWatch 控制台中查看日志。