

# 使用 Lambda 处理 Amazon S3 事件通知
<a name="with-s3"></a>

您可以使用 Lambda 来处理来自 Amazon Simple Storage Service 的[事件通知](https://docs.aws.amazon.com/AmazonS3/latest/userguide/NotificationHowTo.html)。Amazon S3 可以在创建或删除对象时向 Lambda 函数发送事件。您在存储桶上配置通知设置，并向 Amazon S3 授予权限来根据函数的基于资源的权限策略调用函数。

**警告**  
如果您的 Lambda 函数使用触发它的同一存储桶，则会导致在一个循环中运行该函数。例如，如果每当上传一个对象，存储桶就触发某个函数，而该函数又上传一个对象给存储桶，则该函数间接触发了自身。为避免这种情况，请使用两个存储桶，或将触发器配置为仅适用于传入对象所用的前缀。

Amazon S3 使用包含有关对象的详细信息的事件[异步](invocation-async.md)调用您的函数。以下示例显示了在将部署包上载到 Amazon S3 时 Amazon S3 发送的事件。

**Example Amazon S3 通知事件**  

```
{
  "Records": [
    {
      "eventVersion": "2.1",
      "eventSource": "aws:s3",
      "awsRegion": "us-east-2",
      "eventTime": "2019-09-03T19:37:27.192Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "AWS:AIDAINPONIXQXHT3IKHL2"
      },
      "requestParameters": {
        "sourceIPAddress": "205.255.255.255"
      },
      "responseElements": {
        "x-amz-request-id": "D82B88E5F771F645",
        "x-amz-id-2": "vlR7PnpV2Ce81l0PRw6jlUpck7Jo5ZsQjryTjKlc5aLWGVHPZLj5NeC6qMa0emYBDXOo6QBU0Wo="
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "828aa6fc-f7b5-4305-8584-487c791949c1",
        "bucket": {
          "name": "amzn-s3-demo-bucket",
          "ownerIdentity": {
            "principalId": "A3I5XTEXAMAI3E"
          },
          "arn": "arn:aws:s3:::lambda-artifacts-deafc19498e3f2df"
        },
        "object": {
          "key": "b21b84d653bb07b05b1e6b33684dc11b",
          "size": 1305107,
          "eTag": "b21b84d653bb07b05b1e6b33684dc11b",
          "sequencer": "0C0F6F405D6ED209E1"
        }
      }
    }
  ]
}
```

要调用您的函数，Amazon S3 需要来自该函数的[基于资源的策略](access-control-resource-based.md)的权限。当您在 Lambda 控制台中配置 Amazon S3 触发器时，该控制台将修改基于资源的策略以允许 Amazon S3 在存储桶名称和账户 ID 匹配时调用函数。如果您在 Amazon S3 中配置通知，请使用 Lambda API 更新策略。您还可以使用 Lambda API 向另一个账户授予权限，或将权限限制到指定的别名。

如果您的函数使用 AWS 开发工具包来管理 Amazon S3 资源，则其[执行角色](lambda-intro-execution-role.md)也需要 Amazon S3 权限。

**Topics**
+ [教程：使用 Amazon S3 触发器调用 Lambda 函数](with-s3-example.md)
+ [教程：使用 Amazon S3 触发器创建缩略图](with-s3-tutorial.md)

# 教程：使用 Amazon S3 触发器调用 Lambda 函数
<a name="with-s3-example"></a>

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

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3_tut_config.png)


本教程演示如何：

1. 创建 Amazon S3 存储桶。

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

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

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

完成这些步骤后，您将了解如何配置 Lambda 函数，使其在向 Amazon S3 存储桶添加或删除对象时运行。您仅可以使用 AWS 管理控制台 完成此教程。

## 创建 Amazon S3 存储桶
<a name="with-s3-example-create-bucket"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps1.png)


**创建 Amazon S3 存储桶**

1. 打开 [Amazon S3 控制台](https://console.aws.amazon.com/s3)并选择**通用存储桶**页面。

1. 选择最接近您地理位置的 AWS 区域。您可以使用屏幕顶部的下拉列表更改区域。在本教程的后面部分，您必须在同个区域中创建 Lambda 函数。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/console_region_select.png)

1. 选择 **Create bucket**（创建存储桶）。

1. 在 **General configuration**（常规配置）下，执行以下操作：

   1. 对于**存储桶类型**，确保选中**通用型**。

   1. 对于**存储桶名称**，输入符合 Amazon S3 存储桶[命名规则](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html)的全局唯一名称。存储桶名称只能由小写字母、数字、句点（.）和连字符（-）组成。

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

## 将测试对象上传到存储桶
<a name="with-s3-example-upload-test-object"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps2.png)


**要上传测试对象**

1. 打开 Amazon S3 控制台的[存储桶](https://console.aws.amazon.com/s3/buckets)页面，选择您在上一步中创建的存储桶。

1. 选择**上传**。

1. 选择**添加文件**，然后选择要上传的对象。您可以选择任何文件（例如 `HappyFace.jpg`）。

1. 选择**打开**，然后选择**上传**。

在本教程的后面部分，您要使用此对象测试 Lambda 函数。

## 创建权限策略
<a name="with-s3-example-create-policy"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps3.png)


创建权限策略，允许 Lambda 从 Amazon S3 存储桶获取对象并写入 Amazon CloudWatch Logs。

**创建策略**

1. 打开 IAM 控制台的 [Policies（策略）页面](https://console.aws.amazon.com/iam/home#/policies)。

1. 选择**创建策略**。

1. 选择 **JSON** 选项卡，然后将以下自定义策略粘贴到 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:::*/*"
           }
       ]
   }
   ```

------

1. 选择**下一步：标签**。

1. 选择**下一步：审核**。

1. 在 **Review policy (查看策略)** 下，为策略 **Name (名称)** 输入 **s3-trigger-tutorial**。

1. 选择**创建策略**。

## 创建执行角色
<a name="with-s3-example-create-role"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps4.png)


[执行角色](lambda-intro-execution-role.md)是一个 AWS Identity and Access Management（IAM）角色，用于向 Lambda 函数授予访问 AWS 服务 和资源的权限。在此步骤中，您要使用在之前步骤中创建的权限策略来创建执行角色。

**创建执行角色并附加自定义权限策略**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择**创建角色**。

1. 对于可信实体，选择 **AWS 服务**，对于使用案例，选择 **Lambda**。

1. 选择**下一步**。

1. 在策略搜索框中，输入 **s3-trigger-tutorial**。

1. 在搜索结果中，选择您创建的策略（`s3-trigger-tutorial`），然后选择 **Next**（下一步）。

1. 在 **Role details**（角色详细信息）下，为 **Role name**（角色名称）输入 **lambda-s3-trigger-role**，然后选择 **Create role**（创建角色）。

## 创建 Lambda 函数
<a name="with-s3-example-create-function"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps5.png)


使用 Python 3.14 运行时系统在控制台中创建 Lambda 函数。

**创建 Lambda 函数**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 确保您在创建 Amazon S3 存储桶所在的同一 AWS 区域 内操作。您可以使用屏幕顶部的下拉列表更改区域。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/console_region_select.png)

1. 选择**创建函数**。

1. 选择**从头开始编写**。

1. 在**基本信息**中，执行以下操作：

   1. 对于**函数名称**，输入 `s3-trigger-tutorial`。

   1. 对于**运行时系统**，选择 **Python 3.14**。

   1. 对于**架构**，选择 **x86\$164**。

1. 在**更改默认执行角色**选项卡中，执行以下操作：

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

   1. 选择您之前创建的 `lambda-s3-trigger-role`。

1. 选择**创建函数**。

## 部署函数代码
<a name="with-s3-example-deploy-code"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps6.png)


本教程使用 Python 3.14 运行时系统，但我们还提供了适用于其他运行时系统的示例代码文件。您可以选择以下框中的选项卡，查看适用于您感兴趣的运行时系统的代码。

Lambda 函数检索已上传对象的键名称和来自该对象从 Amazon S3 收到的 `event` 参数的存储桶名称。然后，该函数使用 适用于 Python (Boto3) 的 AWS SDK 中的 [get\$1object](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/get_object.html) 方法来检索对象的元数据，包括已上传对象的内容类型（MIME 类型）。

**要部署函数代码**

1. 在下框中选择 **Python** 选项卡并复制代码。

------
#### [ .NET ]

**适用于 .NET 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 .NET 将 S3 事件与 Lambda 结合使用。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   ﻿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 的 SDK V2**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Go 将 S3 事件与 Lambda 结合使用。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   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 ]

**适用于 Java 的 SDK 2.x**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Java 将 S3 事件与 Lambda 结合使用。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   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 ]

**SDK for JavaScript（v3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 JavaScript 将 S3 事件与 Lambda 结合使用。  

   ```
   import { S3Client, HeadObjectCommand } from "@aws-sdk/client-s3";
   
   const client = new S3Client();
   
   export const 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, ' '));
   
       try {
           const { ContentType } = await client.send(new HeadObjectCommand({
               Bucket: bucket,
               Key: key,
           }));
   
           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);
       }
   };
   ```
使用 TypeScript 将 S3 事件与 Lambda 结合使用。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   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);
     }
   };
   ```

------
#### [ PHP ]

**适用于 PHP 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 PHP 将 S3 事件与 Lambda 结合使用。  

   ```
   <?php
   
   use Bref\Context\Context;
   use Bref\Event\S3\S3Event;
   use Bref\Event\S3\S3Handler;
   use Bref\Logger\StderrLogger;
   
   require __DIR__ . '/vendor/autoload.php';
   
   
   class Handler extends S3Handler 
   {
       private StderrLogger $logger;
       public function __construct(StderrLogger $logger)
       {
           $this->logger = $logger;
       }
       
       public function handleS3(S3Event $event, Context $context) : void
       {
           $this->logger->info("Processing S3 records");
   
           // Get the object from the event and show its content type
           $records = $event->getRecords();
           
           foreach ($records as $record) 
           {
               $bucket = $record->getBucket()->getName();
               $key = urldecode($record->getObject()->getKey());
   
               try {
                   $fileSize = urldecode($record->getObject()->getSize());
                   echo "File Size: " . $fileSize . "\n";
                   // TODO: Implement your custom processing logic here
               } catch (Exception $e) {
                   echo $e->getMessage() . "\n";
                   echo 'Error getting object ' . $key . ' from bucket ' . $bucket . '. Make sure they exist and your bucket is in the same region as this function.' . "\n";
                   throw $e;
               }
           }
       }
   }
   
   $logger = new StderrLogger();
   return new Handler($logger);
   ```

------
#### [ Python ]

**适用于 Python 的 SDK（Boto3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Python 将 S3 事件与 Lambda 结合使用。  

   ```
   # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: Apache-2.0
   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
   ```

------
#### [ Ruby ]

**适用于 Ruby 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Ruby 将 S3 事件与 Lambda 结合使用。  

   ```
   require 'json'
   require 'uri'
   require 'aws-sdk'
   
   puts 'Loading function'
   
   def lambda_handler(event:, context:)
     s3 = Aws::S3::Client.new(region: 'region') # Your AWS region
     # puts "Received event: #{JSON.dump(event)}"
   
     # Get the object from the event and show its content type
     bucket = event['Records'][0]['s3']['bucket']['name']
     key = URI.decode_www_form_component(event['Records'][0]['s3']['object']['key'], Encoding::UTF_8)
     begin
       response = s3.get_object(bucket: bucket, key: key)
       puts "CONTENT TYPE: #{response.content_type}"
       return response.content_type
     rescue StandardError => e
       puts e.message
       puts "Error getting object #{key} from bucket #{bucket}. Make sure they exist and your bucket is in the same region as this function."
       raise e
     end
   end
   ```

------
#### [ Rust ]

**适用于 Rust 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Rust 将 S3 事件与 Lambda 结合使用。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   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. 在 Lambda 控制台的**代码源**窗格中，将代码粘贴到代码编辑器中，替换 Lambda 创建的代码。

1. 在**部署**部分，选择**部署**以更新函数的代码：  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/getting-started-tutorial/deploy-console.png)

## 创建 Amazon S3 触发器
<a name="with-s3-example-create-trigger"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps7.png)


**创建 Amazon S3 触发器**

1. 在**函数概述**窗格中，选择**添加触发器**。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/overview-trigger.png)

1. 选择 **S3**。

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

1. 在**事件类型**下，确保已选择**所有对象创建事件**。

1. 在**递归调用**下，选中复选框以确认知晓不建议使用相同的 Amazon S3 存储桶用于输入和输出。

1. 选择**添加**。

**注意**  
当您使用 Lambda 控制台为 Lambda 函数创建 Amazon S3 触发器时，Amazon S3 会在您指定的存储桶上配置[事件通知](https://docs.aws.amazon.com/AmazonS3/latest/userguide/EventNotifications.html)。在配置此事件通知之前，Amazon S3 会执行一系列检查以确认事件目标是否存在并具有所需的 IAM 策略。Amazon S3 还会对为该存储桶配置的任何其他事件通知执行这些测试。  
由于这项检查，如果存储桶之前为已不再存在的资源或没有所需权限策略的资源配置了事件目标，则 Amazon S3 将无法创建新的事件通知。您将看到以下错误消息，表明无法创建触发器：  

```
An error occurred when creating the trigger: Unable to validate the following destination configurations.
```
如果您之前使用同一存储桶为另一个 Lambda 函数配置了触发器，并且此后又删除了该函数或修改了其权限策略，则会看到此错误。

## 使用虚拟事件测试 Lambda 函数
<a name="with-s3-example-test-dummy-event"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps8.png)


**要使用虚拟事件测试 Lambda 函数**

1. 在函数的 Lambda 控制台页面中，选择**测试**选项卡。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/test-tab.png)

1. 对于**事件名称**，输入 `MyTestEvent`。

1. 在**事件 JSON** 中，粘贴以下测试事件。请务必替换以下值：
   + 使用 `us-east-1` 替换要在其中创建 Amazon S3 存储桶的区域。
   + 将 `amzn-s3-demo-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": "amzn-s3-demo-bucket",
             "ownerIdentity": {
               "principalId": "EXAMPLE"
             },
             "arn": "arn:aws:s3:::amzn-s3-demo-bucket"
           },
           "object": {
             "key": "test%2Fkey",
             "size": 1024,
             "eTag": "0123456789abcdef0123456789abcdef",
             "sequencer": "0A1B2C3D4E5F678901"
           }
         }
       }
     ]
   }
   ```

1. 选择**保存**。

1. 选择**测试**。

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

   ```
   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: 'amzn-s3-demo-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 函数
<a name="with-s3-example-test-s3-trigger"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps9.png)


要使用配置的触发器测试函数，请使用控制台将对象上传到 Amazon S3 存储桶。要验证 Lambda 函数是否按预期运行，请使用 CloudWatch Logs 查看函数的输出。

**要将对象上传到 Amazon S3 存储桶**

1. 打开 Amazon S3 控制台的[存储桶](https://console.aws.amazon.com/s3/buckets)页面，选择之前创建的存储桶。

1. 选择**上传**。

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

1. 选择**打开**，然后选择**上传**。

**使用 CloudWatch Logs 验证函数调用情况**

1. 打开 [CloudWatch 控制台](https://console.aws.amazon.com/cloudwatch/home)。

1. 确保您在创建 Lambda 函数所在相同的 AWS 区域 操作。您可以使用屏幕顶部的下拉列表更改区域。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/console_region_select.png)

1. 选择**日志**，然后选择**日志组**。

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

1. 在**日志流**下，选择最新的日志流。

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

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

## 清除资源
<a name="cleanup"></a>

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

**删除 Lambda 函数**

1. 打开 Lamba 控制台的 [Functions（函数）页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择您创建的函数。

1. 依次选择**操作**和**删除**。

1. 在文本输入字段中键入 **confirm**，然后选择**删除**。

**删除执行角色**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

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

1. 选择**删除**。

1. 在文本输入字段中输入角色名称，然后选择 **Delete**（删除）。

**删除 S3 存储桶**

1. 打开 [Amazon S3 控制台](https://console.aws.amazon.com//s3/home#)。

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

1. 选择**删除**。

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

1. 选择**删除存储桶**。

## 后续步骤
<a name="next-steps"></a>

在 [教程：使用 Amazon S3 触发器创建缩略图](with-s3-tutorial.md) 中，Amazon S3 触发器会调用一个函数，该函数会为上传到存储桶的每个图像文件创建缩略图。本教程需要适度的AWS和 Lambda 领域知识水平。其展示了如何使用 AWS Command Line Interface（AWS CLI）来创建资源，以及如何为函数及其依赖项创建 .zip 文件存档部署包。

# 教程：使用 Amazon S3 触发器创建缩略图
<a name="with-s3-tutorial"></a>

在本教程中，您将创建并配置 Lambda 函数，用于调整添加到 Amazon Simple Storage Service（Amazon S3）存储桶的图像大小。当您向存储桶添加图像文件时，Amazon S3 会调用您的 Lambda 函数。然后，该函数会创建图像的缩略图版本，并将其输出到不同的 Amazon S3 存储桶。

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_resources.png)


要完成本教程，请执行以下步骤：

1. 创建源和目标 Amazon S3 存储桶并上传示例图片。

1. 创建一个 Lambda 函数，用于调整图像大小并将缩略图输出到 Amazon S3 存储桶。

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

1. 若要测试您的函数，先使用虚拟事件，然后将图像上传到源存储桶。

完成这些步骤后，您将了解如何使用 Lambda 对添加到 Amazon S3 存储桶的对象执行文件处理任务。您可以使用 AWS Command Line Interface 或 AWS CLI (AWS 管理控制台) 完成此教程。

如果您相同通过更简单的示例来了解如何为 Lambda 配置 Amazon S3 触发器，则可以参阅[教程：使用 Amazon S3 触发器调用 Lambda 函数](https://docs.aws.amazon.com/lambda/latest/dg/with-s3-example.html)。

**Topics**
+ [先决条件](#with-s3-example-prereqs)
+ [创建两个 Amazon S3 存储桶](#with-s3-tutorial-prepare-create-buckets)
+ [将测试图片上传到源存储桶](#with-s3-tutorial-test-image)
+ [创建权限策略](#with-s3-tutorial-create-policy)
+ [创建执行角色](#with-s3-tutorial-create-execution-role)
+ [创建函数部署包](#with-s3-tutorial-create-function-package)
+ [创建 Lambda 函数](#with-s3-tutorial-create-function-createfunction)
+ [配置 Amazon S3 来调用函数](#with-s3-tutorial-configure-s3-trigger)
+ [使用虚拟事件测试 Lambda 函数](#with-s3-tutorial-dummy-test)
+ [使用 Amazon S3 触发器测试函数](#with-s3-tutorial-test-s3)
+ [清除资源](#s3-tutorial-cleanup)

## 先决条件
<a name="with-s3-example-prereqs"></a>

如果您想使用 AWS CLI 来完成教程，请安装[最新版本的 AWS Command Line Interface]()。

对于 Lambda 函数代码，您可以使用 Python 或 Node.js。为您要使用的语言安装语言支持工具和软件包管理器。

### 安装 AWS Command Line Interface
<a name="install_aws_cli"></a>

如果您尚未安装 AWS Command Line Interface，请按照[安装或更新最新版本的 AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 中的步骤进行安装。

本教程需要命令行终端或 Shell 来运行命令。在 Linux 和 macOS 中，可使用您首选的 Shell 和程序包管理器。

**注意**  
在 Windows 中，操作系统的内置终端不支持您经常与 Lambda 一起使用的某些 Bash CLI 命令（例如 `zip`）。[安装 Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10)，获取 Ubuntu 和 Bash 与 Windows 集成的版本。

## 创建两个 Amazon S3 存储桶
<a name="with-s3-tutorial-prepare-create-buckets"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps1.png)


先创建两个 Amazon S3 存储桶。第一个存储桶是您要向其上传图像的源存储桶。第二个存储桶是 Lambda 用来保存调用函数时调整大小后的缩略图。

------
#### [ AWS 管理控制台 ]

**创建 Amazon S3 存储桶（控制台）**

1. 打开 [Amazon S3 控制台](https://console.aws.amazon.com/s3)并选择**通用存储桶**页面。

1. 选择最接近您地理位置的 AWS 区域。您可以使用屏幕顶部的下拉列表更改区域。在本教程的后面部分，您必须在同个区域中创建 Lambda 函数。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/console_region_select.png)

1. 选择 **Create bucket**（创建存储桶）。

1. 在 **General configuration**（常规配置）下，执行以下操作：

   1. 对于**存储桶类型**，确保选中**通用型**。

   1. 对于**存储桶名称**，输入符合 Amazon S3 存储桶[命名规则](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html)的全局唯一名称。存储桶名称只能由小写字母、数字、句点（.）和连字符（-）组成。

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

1. 重复步骤 1 到 5 以创建自己的目标存储桶。在**存储桶名称**中输入 `amzn-s3-demo-source-bucket-resized`，其中 `amzn-s3-demo-source-bucket` 是您刚刚创建的源存储桶的名称。

------
#### [ AWS CLI ]

**创建 Amazon S3 存储桶（AWS CLI）**

1. 运行以下 CLI 命令来创建自己的源存储桶。您为存储桶选择的名称必须具有全局唯一性，并遵守 Amazon S3 [存储桶命名规则](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html)。名称只能由小写字母、数字、句点（.）和连字符（-）组成。对于 `region` 和 `LocationConstraint`，请选择最接近您地理位置的 [AWS 区域](https://docs.aws.amazon.com/general/latest/gr/lambda-service.html)。

   ```
   aws s3api create-bucket --bucket amzn-s3-demo-source-bucket --region us-east-1 \
   --create-bucket-configuration LocationConstraint=us-east-1
   ```

   在本教程的后面部分，您必须在与源存储桶相同的 AWS 区域 中创建 Lambda 函数，因此请记下您选择的区域。

1. 运行以下命令来创建自己的目标存储桶。对于存储桶名称，必须使用 `amzn-s3-demo-source-bucket-resized`，其中 `amzn-s3-demo-source-bucket` 是您在步骤 1 中创建的源存储桶名称。对于 `region` 和 `LocationConstraint`，请选择与用于创建源存储桶时相同的 AWS 区域。

   ```
   aws s3api create-bucket --bucket amzn-s3-demo-source-bucket-resized --region us-east-1 \
   --create-bucket-configuration LocationConstraint=us-east-1
   ```

------

## 将测试图片上传到源存储桶
<a name="with-s3-tutorial-test-image"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps2.png)


在本教程的后面部分，您将使用 AWS CLI 或 Lambda 控制台调用 Lambda 函数以对其进行测试。要确认函数是否正常运行，您的源存储桶需要包含测试图片。此图像可以是您选择的任何 JPG 或 PNG 文件。

------
#### [ AWS 管理控制台 ]

**将测试图片上传到源存储桶（控制台）**

1. 打开 Amazon S3 控制台的[存储桶](https://console.aws.amazon.com/s3/buckets)页面。

1. 选择您在上一步中创建的源存储桶。

1. 选择**上传**。

1. 选择**添加文件**，然后使用文件选择器选择要上传的对象。

1. 选择**打开**，然后选择**上传**。

------
#### [ AWS CLI ]

**将测试图片上传到源存储桶（AWS CLI）**
+ 在包含要上传的图像的目录中，运行以下 CLI 命令。将 `--bucket` 参数替换为源存储桶的名称。对于`--key` 和 `--body` 参数，请使用测试图像的文件名。

  ```
  aws s3api put-object --bucket amzn-s3-demo-source-bucket --key HappyFace.jpg --body ./HappyFace.jpg
  ```

------

## 创建权限策略
<a name="with-s3-tutorial-create-policy"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps3.png)


创建 Lambda 函数的第一步是创建权限策略。此策略为函数提供访问其他 AWS 资源所需的权限。在本教程中，此策略授予了 Lambda 对 Amazon S3 存储桶的读取和写入权限，并允许其写入 Amazon CloudWatch Logs。

------
#### [ AWS 管理控制台 ]

**创建策略（控制台）**

1. 打开 AWS Identity and Access Management IAM 控制台的 [Policies（策略）页面](https://console.aws.amazon.com/iamv2/home#policies)。

1. 选择**创建策略**。

1. 选择 **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:::*/*"
           },
           {
               "Effect": "Allow",
               "Action": [
                   "s3:PutObject"
               ],
               "Resource": "arn:aws:s3:::*/*"
           }
       ]
   }
   ```

1. 选择**下一步**。

1. 在**策略详细信息**下，为**策略名称**输入 `LambdaS3Policy`。

1. 选择**创建策略**。

------
#### [ AWS CLI ]

**创建策略（AWS CLI）**

1. 将下列 JSON 保存在名为 `policy.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:::*/*"
           },
           {
               "Effect": "Allow",
               "Action": [
                   "s3:PutObject"
               ],
               "Resource": "arn:aws:s3:::*/*"
           }
       ]
   }
   ```

1. 在保存 JSON 策略文档的目录中，运行以下 CLI 命令。

   ```
   aws iam create-policy --policy-name LambdaS3Policy --policy-document file://policy.json
   ```

------

## 创建执行角色
<a name="with-s3-tutorial-create-execution-role"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps4.png)


执行角色是一个 IAM 角色，用于向 Lambda 函数授予访问 AWS 服务 和资源的权限。要授予函数读取和写入 Amazon S3 存储桶的权限，您需要附加上一步中创建的权限策略。

------
#### [ AWS 管理控制台 ]

**创建执行角色并附加权限策略（控制台）**

1. 打开（IAM）控制台的[角色](https://console.aws.amazon.com/iamv2/home#roles)页面。

1. 选择**创建角色**。

1. 在**可信实体类型**中选择 **AWS 服务**，在**使用案例**中选择 **Lambda**。

1. 选择**下一步**。

1. 执行以下操作添加您在上一步中创建的权限策略：

   1. 在策略搜索框中，输入 `LambdaS3Policy`。

   1. 在搜索结果中，选中 `LambdaS3Policy` 的复选框。

   1. 选择**下一步**。

1. 在**角色详细信息**下的**角色名称**中输入 `LambdaS3Role`。

1. 选择**创建角色**。

------
#### [ AWS CLI ]

**创建执行角色并附加权限策略（AWS CLI）**

1. 将下列 JSON 保存在名为 `trust-policy.json` 的文件中。此信任策略允许 Lambda 通过向服务主体 `lambda.amazonaws.com` 授予调用 AWS Security Token Service（AWS STS）`AssumeRole` 操作的权限来使用该角色的权限。  
****  

   ```
   {
     "Version":"2012-10-17",		 	 	 
     "Statement": [
       {
         "Effect": "Allow",
         "Principal": {
           "Service": "lambda.amazonaws.com"
         },
         "Action": "sts:AssumeRole"
       }
     ]
   }
   ```

1. 在保存 JSON 信任策略文档的目录中，运行以下 CLI 命令来创建执行角色。

   ```
   aws iam create-role --role-name LambdaS3Role --assume-role-policy-document file://trust-policy.json
   ```

1. 要附加您在上一步中创建的权限策略，请运行以下 CLI 命令。将策略 ARN 中的 AWS 账户 号码替换为自己的账号。

   ```
   aws iam attach-role-policy --role-name LambdaS3Role --policy-arn arn:aws:iam::123456789012:policy/LambdaS3Policy
   ```

------

## 创建函数部署包
<a name="with-s3-tutorial-create-function-package"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps5.png)


要创建函数，您需要创建包含函数代码和所有依赖项的*部署包*。对于此 `CreateThumbnail` 函数，您的函数代码使用单独的库来调整图像大小。按照所选语言的说明创建包含所需库的部署包。

------
#### [ Node.js ]

**创建部署包（Node.js）**

1. 为您的函数代码和依赖项创建一个名为 `lambda-s3` 的目录并导航到此目录。

   ```
   mkdir lambda-s3
   cd lambda-s3
   ```

1. 使用 `npm` 创建新的 Node.js 项目。要在交互式体验中接受提供的默认选项，请按 `Enter`。

   ```
   npm init
   ```

1. 将以下函数代码保存在名为 `index.mjs` 的文件中。请务必将 `us-east-1` 替换您创建源存储桶和目标存储桶时所在的 AWS 区域。

   ```
   // dependencies
   import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
   
   import { Readable } from 'stream';
   
   import sharp from 'sharp';
   import util from 'util';
   
   
   // create S3 client
   const s3 = new S3Client({region: 'us-east-1'});
   
   // define the handler function
   export const handler = async (event, context) => {
   
   // Read options from the event parameter and get the source bucket
   console.log("Reading options from event:\n", util.inspect(event, {depth: 5}));
     const srcBucket = event.Records[0].s3.bucket.name;
     
   // Object key may have spaces or unicode non-ASCII characters
   const srcKey    = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " "));
   const dstBucket = srcBucket + "-resized";
   const dstKey    = "resized-" + srcKey;
   
   // Infer the image type from the file suffix
   const typeMatch = srcKey.match(/\.([^.]*)$/);
   if (!typeMatch) {
     console.log("Could not determine the image type.");
     return;
   }
   
   // Check that the image type is supported
   const imageType = typeMatch[1].toLowerCase();
   if (imageType != "jpg" && imageType != "png") {
     console.log(`Unsupported image type: ${imageType}`);
     return;
   }
   
   // Get the image from the source bucket. GetObjectCommand returns a stream.
   try {
     const params = {
       Bucket: srcBucket,
       Key: srcKey
     };
     var response = await s3.send(new GetObjectCommand(params));
     var stream = response.Body;
     
   // Convert stream to buffer to pass to sharp resize function.
     if (stream instanceof Readable) {
       var content_buffer = Buffer.concat(await stream.toArray());
       
     } else {
       throw new Error('Unknown object stream type');
     }
   
   
   } catch (error) {
     console.log(error);
     return;
   }
   
     
   // set thumbnail width. Resize will set the height automatically to maintain aspect ratio.
   const width  = 200;
   
   // Use the sharp module to resize the image and save in a buffer.
   try {    
     var output_buffer = await sharp(content_buffer).resize(width).toBuffer();
   
   } catch (error) {
     console.log(error);
     return;
   }
   
   // Upload the thumbnail image to the destination bucket
   try {
     const destparams = {
       Bucket: dstBucket,
       Key: dstKey,
       Body: output_buffer,
       ContentType: "image"
     };
   
     const putResult = await s3.send(new PutObjectCommand(destparams));
   
     } catch (error) {
       console.log(error);
       return;
     }
   
     console.log('Successfully resized ' + srcBucket + '/' + srcKey +
       ' and uploaded to ' + dstBucket + '/' + dstKey);
     };
   ```

1. 在 `lambda-s3` 目录中，使用 npm 安装 sharp 库。请注意，最新版本的 sharp（0.33）与 Lambda 不兼容。安装版本 0.32.6 以完成本教程。

   ```
   npm install sharp@0.32.6
   ```

   npm `install` 命令会为模块创建 `node_modules` 目录。完成此步骤后，目录结构应如下所示：

   ```
   lambda-s3
   |- index.mjs
   |- node_modules
   |  |- base64js
   |  |- bl
   |  |- buffer
   ...
   |- package-lock.json
   |- package.json
   ```

1. 创建一个包含函数代码和依赖项的部署包。在 MacOS 或 Linux 中，运行以下命令。

   ```
   zip -r function.zip .
   ```

   在 Windows 中，使用您首选的 ZIP 实用工具来创建 .zip 文件。确保您的 `index.mjs`、`package.json`、和 `package-lock.json` 文件以及您的 `node_modules` 目录都在.zip 文件的根目录中。

------
#### [ Python ]

**创建部署包（Python）**

1. 将代码示例保存为名为 `lambda_function.py` 的文件。

   ```
   import boto3
   import os
   import sys
   import uuid
   from urllib.parse import unquote_plus
   from PIL import Image
   import PIL.Image
               
   s3_client = boto3.client('s3')
               
   def resize_image(image_path, resized_path):
     with Image.open(image_path) as image:
       image.thumbnail(tuple(x / 2 for x in image.size))
       image.save(resized_path)
               
   def lambda_handler(event, context):
     for record in event['Records']:
       bucket = record['s3']['bucket']['name']
       key = unquote_plus(record['s3']['object']['key'])
       tmpkey = key.replace('/', '')
       download_path = '/tmp/{}{}'.format(uuid.uuid4(), tmpkey)
       upload_path = '/tmp/resized-{}'.format(tmpkey)
       s3_client.download_file(bucket, key, download_path)
       resize_image(download_path, upload_path)
       s3_client.upload_file(upload_path, '{}-resized'.format(bucket), 'resized-{}'.format(key))
   ```

1. 在创建 `lambda_function.py` 文件的同一目录中，创建一个名为 `package` 的新目录并安装 [Pillow（PIL）](https://pypi.org/project/Pillow/)库和 适用于 Python (Boto3) 的 AWS SDK。虽然 Lambda Python 运行时系统包含 Boto3 SDK 的一个版本，但建议您将所有函数的依赖项添加到部署包中，即使这些依赖项已经包含在了运行时系统中。有关更多信息，请参阅 [Python 中的运行时系统依赖项](https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-dependencies)。

   ```
   mkdir package
   pip install \
   --platform manylinux2014_x86_64 \
   --target=package \
   --implementation cp \
   --python-version 3.12 \
   --only-binary=:all: --upgrade \
   pillow boto3
   ```

   Pillow 库包含 C/C\$1\$1 代码。通过使用 `--platform manylinux_2014_x86_64` 和 `--only-binary=:all:` 选项，pip 将下载并安装包含与 Amazon Linux 2 操作系统兼容的预编译二进制文件的 Pillow 版本。这可以确保无论本地构建计算机的操作系统和架构如何，部署包都能在 Lambda 执行环境中正常发挥作用。

1. 创建包含应用程序代码和 Pillow 以及 Boto3 库的 zip 文件。在 Linux 或 MacOS 中，从命令行界面运行以下命令。

   ```
   cd package
   zip -r ../lambda_function.zip .
   cd ..
   zip lambda_function.zip lambda_function.py
   ```

    在 Windows 中，使用您首选的压缩工具来创建 `lambda_function.zip` 文件。确保您的 `lambda_function.py` 文件和包含依赖项的文件夹都位于.zip 文件的根目录下。

您也可以使用 Python 虚拟环境创建部署包。请参阅 [将 .zip 文件归档用于 Python Lambda 函数](python-package.md)。

------

## 创建 Lambda 函数
<a name="with-s3-tutorial-create-function-createfunction"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps6.png)


您也可以使用 AWS CLI 或 Lambda 控制台创建 Lambda 函数。按照所选语言的说明创建函数。

------
#### [ AWS 管理控制台 ]

**创建函数（控制台）**

要使用控制台创建 Lambda 函数，首先要创建包含一些“Hello world”代码的基本函数。然后，通过上传在上一步中创建的 .zip 或 JAR 文件，将此代码替换为自己的函数代码。

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 确保您在创建 Amazon S3 存储桶所在的同一 AWS 区域 内操作。您可以使用屏幕顶部的下拉列表更改区域。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/console_region_select.png)

1. 选择**创建函数**。

1. 选择**从头开始创作**。

1. 在**基本信息**中，执行以下操作：

   1. 对于 **Function name（函数名称）**，请输入 `CreateThumbnail`。

   1. 对于**运行时**，根据您为函数选择的语言，选择 **Node.js 22.x** 或 **Python 3.12**。

   1. 对于**架构**，选择 **x86\$164**。

1. 在**更改默认执行角色**选项卡中，执行以下操作：

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

   1. 选择您之前创建的 `LambdaS3Role`。

1. 选择**创建函数**。

**上传函数代码（控制台）**

1. 在**代码源**窗格中，选择**上传自**。

1. 选择 **.zip 文件**。

1. 选择**上传**。

1. 在文件选择器中，选择 .zip 文件，然后选择**打开**。

1. 选择**保存**。

------
#### [ AWS CLI ]

**创建函数（AWS CLI）**
+ 运行您所选语言的 CLI 命令。对于 `role` 参数，确保将 `123456789012` 替换为自己的 AWS 账户 ID。对于 `region` 参数，将 `us-east-1` 替换为在其中创建 Amazon S3 存储桶的区域。
  + 对于 **Node.js**，从包含 `function.zip` 文件的目录中运行以下命令。

    ```
    aws lambda create-function --function-name CreateThumbnail \
    --zip-file fileb://function.zip --handler index.handler --runtime nodejs24.x \
    --timeout 10 --memory-size 1024 \
    --role arn:aws:iam::123456789012:role/LambdaS3Role --region us-east-1
    ```
  + 对于 **Python**，从包含 `lambda_function.zip` 文件的目录中运行以下命令。

    ```
    aws lambda create-function --function-name CreateThumbnail \
    --zip-file fileb://lambda_function.zip --handler lambda_function.lambda_handler \
    --runtime python3.14 --timeout 10 --memory-size 1024 \
    --role arn:aws:iam::123456789012:role/LambdaS3Role --region us-east-1
    ```

------

## 配置 Amazon S3 来调用函数
<a name="with-s3-tutorial-configure-s3-trigger"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps7.png)


要在将图像上传到源存储桶时运行 Lambda 函数，您需要为函数配置触发器。您可以使用控制台或 AWS CLI 配置 Amazon S3 触发器。

**重要**  
此程序将 Amazon S3 存储桶配置为每次在此存储桶中创建对象时调用您的函数。请确保仅在源存储桶上配置。如果您的 Lambda 函数在调用此函数的同一个存储桶中创建对象，则可以[在循环中持续调用](https://serverlessland.com/content/service/lambda/guides/aws-lambda-operator-guide/recursive-runaway)您的函数。这可能会导致您的 AWS 账户 产生额外费用。

------
#### [ AWS 管理控制台 ]

**配置 Amazon S3 触发器（控制台）**

1. 打开 Lambda 控制台的[函数页面](https://console.aws.amazon.com/lambda/home#/functions)，然后选择函数 (`CreateThumbnail`)。

1. 选择**添加触发器**。

1. 选择 **S3**。

1. 在**存储桶**下，选择自己的源存储桶。

1. 在**事件类型**下，选择**所有对象创建事件**。

1. 在**递归调用**下，选中复选框以确认知晓不建议使用相同的 Amazon S3 存储桶用于输入和输出。您可以阅读 Serverless Land 中的 [Recursive patterns that cause run-away Lambda functions](https://serverlessland.com/content/service/lambda/guides/aws-lambda-operator-guide/recursive-runaway)，进一步了解 Lambda 中的递归调用模式。

1. 选择**添加**。

   在您使用 Lambda 控制台创建触发器时，Lambda 会自动创建[基于资源的策略](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html)，授予您选择的服务调用函数的权限。

------
#### [ AWS CLI ]

**配置 Amazon S3 触发器（AWS CLI）**

1. 要让 Amazon S3 源存储桶在添加图像文件时调用函数，您首先需要使用[基于资源的策略](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html)为函数配置权限。基于资源的策略声明授予其他 AWS 服务 调用您函数的权限。要授予 Amazon S3 调用函数的权限，请运行以下 CLI 命令。请务必将 `source-account` 参数替换为您的 AWS 账户 ID 并使用自己的源存储桶名称。

   ```
   aws lambda add-permission --function-name CreateThumbnail \
   --principal s3.amazonaws.com --statement-id s3invoke --action "lambda:InvokeFunction" \
   --source-arn arn:aws:s3:::amzn-s3-demo-source-bucket \
   --source-account 123456789012
   ```

   您使用此命令定义的策略允许 Amazon S3 仅在源存储桶上执行操作时调用函数。
**注意**  
虽然 Amazon S3 存储桶名称具有全局唯一性，但在使用基于资源的策略时，最佳做法是指定存储桶必须属于您的账户。这是因为，如果删除一个存储桶，则另一个 AWS 账户 账户可能会创建具有相同 Amazon 资源名称（ARN）的存储桶。

1. 将下列 JSON 保存在名为 `notification.json` 的文件中。在应用到您的源存储桶时，此 JSON 会将存储桶配置为在每次添加新对象时向 Lambda 函数发送通知。将 Lambda 函数 ARN 中的 AWS 账户 号码和 AWS 区域 替换为您自己的账号和区域。

   ```
   {
   "LambdaFunctionConfigurations": [
       {
         "Id": "CreateThumbnailEventConfiguration",
         "LambdaFunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:CreateThumbnail",
         "Events": [ "s3:ObjectCreated:Put" ]
       }
     ]
   }
   ```

1. 运行以下 CLI 命令，将您创建的 JSON 文件中的通知设置应用到源存储桶。将 `amzn-s3-demo-source-bucket` 替换为源存储桶的名称。

   ```
   aws s3api put-bucket-notification-configuration --bucket amzn-s3-demo-source-bucket \
   --notification-configuration file://notification.json
   ```

   要了解有关 `put-bucket-notification-configuration` 命令和 `notification-configuration` 选项的更多信息，请参阅 *AWS CLI 命令参考*中的 [put-bucket-notification-configuration](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-bucket-notification-configuration.html)。

------

## 使用虚拟事件测试 Lambda 函数
<a name="with-s3-tutorial-dummy-test"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps8.png)


在通过向 Amazon S3 源存储桶添加图像文件来测试整个设置之前，您可以通过使用虚拟事件调用 Lambda 函数来测试此函数是否正常运行。Lambda 中的事件是 JSON 格式的文档，其中包含要处理的函数数据。Amazon S3 调用您的函数时，发送到函数的事件包含存储桶名称、存储桶 ARN 和对象键等信息。

------
#### [ AWS 管理控制台 ]

**使用虚拟事件测试 Lambda 函数（控制台）**

1. 打开 Lambda 控制台的[函数页面](https://console.aws.amazon.com/lambda/home#/functions)，然后选择函数 (`CreateThumbnail`)。

1. 选择**测试**选项卡。

1. 要创建测试事件，在**测试事件**窗格中，执行以下操作：

   1. 在**测试事件操作**下，选择**创建新事件**。

   1. 对于**事件名称**，输入 **myTestEvent**。

   1. 在**模板**中，选择 **S3 Put**。

   1. 将以下参数的值替换为您自己的值。
      + 对于 `awsRegion`，将 `us-east-1` 替换为在其中创建 Amazon S3 存储桶的 AWS 区域。
      + 对于 `name`，将 `amzn-s3-demo-bucket` 替换为您自己的 Amazon S3 源存储桶的名称。
      + 对于 `key`，将 `test%2Fkey` 替换为您在步骤 [将测试图片上传到源存储桶](#with-s3-tutorial-test-image) 中上传到源存储桶的测试对象的文件名。

      ```
      {
        "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": "amzn-s3-demo-bucket",
                "ownerIdentity": {
                  "principalId": "EXAMPLE"
                },
                "arn": "arn:aws:s3:::amzn-s3-demo-bucket"
              },
              "object": {
                "key": "test%2Fkey",
                "size": 1024,
                "eTag": "0123456789abcdef0123456789abcdef",
                "sequencer": "0A1B2C3D4E5F678901"
              }
            }
          }
        ]
      }
      ```

   1. 选择**保存**。

1. 在**测试事件**窗格中，选择**测试**。

1. 要检查您的函数是否已创建图像的调整大小版本并将其存储在目标 Amazon S3 存储桶中，请执行以下操作：

   1. 打开 Amazon S3 控制台的[存储桶页面](https://console.aws.amazon.com/s3/buckets)。

   1. 选择目标存储桶并确认调整大小的文件已在**对象**窗格中列出。

------
#### [ AWS CLI ]

**使用虚拟事件测试 Lambda 函数（AWS CLI）**

1. 将下列 JSON 保存在名为 `dummyS3Event.json` 的文件中。将以下参数的值替换为您自己的值：
   + 对于 `awsRegion`，将 `us-east-1` 替换为在其中创建 Amazon S3 存储桶的 AWS 区域。
   + 对于 `name`，将 `amzn-s3-demo-bucket` 替换为您自己的 Amazon S3 源存储桶的名称。
   + 对于 `key`，将 `test%2Fkey` 替换为您在步骤 [将测试图片上传到源存储桶](#with-s3-tutorial-test-image) 中上传到源存储桶的测试对象的文件名。

   ```
   {
     "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": "amzn-s3-demo-bucket",
             "ownerIdentity": {
               "principalId": "EXAMPLE"
             },
             "arn": "arn:aws:s3:::amzn-s3-demo-bucket"
           },
           "object": {
             "key": "test%2Fkey",
             "size": 1024,
             "eTag": "0123456789abcdef0123456789abcdef",
             "sequencer": "0A1B2C3D4E5F678901"
           }
         }
       }
     ]
   }
   ```

1. 在保存 `dummyS3Event.json` 文件的目录中，运行以下 CLI 命令来调用函数。此命令通过指定 `RequestResponse` 作为调用类型参数的值来同步调用 Lambda 函数。要了解有关同步和异步调用的更多信息，请参阅[调用 Lambda 函数](https://docs.aws.amazon.com/lambda/latest/dg/lambda-invocation.html)。

   ```
   aws lambda invoke --function-name CreateThumbnail \
   --invocation-type RequestResponse --cli-binary-format raw-in-base64-out \
   --payload file://dummyS3Event.json outputfile.txt
   ```

   如果您使用的是 AWS CLI 版本 2，则 cli-binary-format 选项必填。要将其设为默认设置，请运行 `aws configure set cli-binary-format raw-in-base64-out`。有关更多信息，请参阅[AWS CLI 支持的全局命令行选项](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-options.html#cli-configure-options-list)。

1. 验证您的函数是否已创建图像的缩略图版本并已保存到目标 Amazon S3 存储桶中。运行以下 CLI 命令，将 `amzn-s3-demo-source-bucket-resized` 替换为自己的目标存储桶的名称。

   ```
   aws s3api list-objects-v2 --bucket amzn-s3-demo-source-bucket-resized
   ```

   您应该可以看到类似于如下所示的输出内容。`Key` 参数显示调整大小后的图像文件的文件名。

   ```
   {
       "Contents": [
           {
               "Key": "resized-HappyFace.jpg",
               "LastModified": "2023-06-06T21:40:07+00:00",
               "ETag": "\"d8ca652ffe83ba6b721ffc20d9d7174a\"",
               "Size": 2633,
               "StorageClass": "STANDARD"
           }
       ]
   }
   ```

------

## 使用 Amazon S3 触发器测试函数
<a name="with-s3-tutorial-test-s3"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps9.png)


此时，您已确认 Lambda 函数运行正常，您可以通过向 Amazon S3 源存储桶添加图像文件来测试完整设置。将图像添加到源存储桶时，应自动调用 Lambda 函数。函数会创建文件已调整大小的版本并将其存储在目标存储桶中。

------
#### [ AWS 管理控制台 ]

**使用 Amazon S3 触发器测试 Lambda 函数（控制台）**

1. 要将图像上传到 Amazon S3 存储桶，请执行以下操作：

   1. 打开 Amazon S3 控制台的[存储桶](https://console.aws.amazon.com/s3/buckets)页面，然后选择您的源存储桶。

   1. 选择**上传**。

   1. 选择**添加文件**，然后使用文件选择器选择要上传的图像文件。您的图像对象可以是任何.jpg 或.png 文件。

   1. 选择**打开**，然后选择**上传**。

1. 执行以下操作，验证 Lambda 是否已将调整大小后的图像文件保存在目标存储桶中：

   1. 导航回 Amazon S3 控制台的[存储桶](https://console.aws.amazon.com/s3/buckets)页面，然后选择您的目标存储桶。

   1. 在**对象**窗格中，您现在应该能看到两个已调整大小的图像文件，其中一个来自 Lambda 函数的每次测试。要下载已调整大小的图像，请选择所需文件，然后选择**下载**。

------
#### [ AWS CLI ]

**使用 Amazon S3 触发器测试 Lambda 函数（AWS CLI）**

1. 在包含要上传的图像的目录中，运行以下 CLI 命令。将 `--bucket` 参数替换为源存储桶的名称。对于`--key` 和 `--body` 参数，请使用测试图像的文件名。您的测试图像可以是任何.jpg 或.png 文件。

   ```
   aws s3api put-object --bucket amzn-s3-demo-source-bucket --key SmileyFace.jpg --body ./SmileyFace.jpg
   ```

1. 验证您的函数是否已创建图像的缩略图版本并已保存到目标 Amazon S3 存储桶中。运行以下 CLI 命令，将 `amzn-s3-demo-source-bucket-resized` 替换为自己的目标存储桶的名称。

   ```
   aws s3api list-objects-v2 --bucket amzn-s3-demo-source-bucket-resized
   ```

   如果函数成功运行，您将看到类似于以下内容的输出。您的目标存储桶现在应该包含两个已调整大小的文件。

   ```
   {
       "Contents": [
           {
               "Key": "resized-HappyFace.jpg",
               "LastModified": "2023-06-07T00:15:50+00:00",
               "ETag": "\"7781a43e765a8301713f533d70968a1e\"",
               "Size": 2763,
               "StorageClass": "STANDARD"
           },
           {
               "Key": "resized-SmileyFace.jpg",
               "LastModified": "2023-06-07T00:13:18+00:00",
               "ETag": "\"ca536e5a1b9e32b22cd549e18792cdbc\"",
               "Size": 1245,
               "StorageClass": "STANDARD"
           }
       ]
   }
   ```

------

## 清除资源
<a name="s3-tutorial-cleanup"></a>

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

**删除 Lambda 函数**

1. 打开 Lamba 控制台的 [Functions（函数）页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择您创建的函数。

1. 依次选择**操作**和**删除**。

1. 在文本输入字段中键入 **confirm**，然后选择 **Delete**（删除）。

**删除您创建的策略**

1. 打开 IAM 控制台的 [Policies（策略）页面](https://console.aws.amazon.com/iam/home#/policies)。

1. 选择您创建的策略 (**AWSLambdaS3Policy**)。

1. 选择 **Policy actions**（策略操作）、**Delete**（删除）。

1. 选择 **Delete**（删除）。

**删除执行角色**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

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

1. 选择**删除**。

1. 在文本输入字段中输入角色名称，然后选择 **Delete**（删除）。

**删除 S3 存储桶**

1. 打开 [Amazon S3 控制台](https://console.aws.amazon.com//s3/home#)。

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

1. 选择**删除**。

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

1. 选择**删除存储桶**。