创建无服务器文件处理应用程序 - AWS Lambda

创建无服务器文件处理应用程序

Lambda 的一个常见用例是执行文件处理任务。例如,使用 Lambda 函数从 HTML 文件或图像自动创建 PDF 文件,或者在用户上传图像时创建缩略图。

在本示例中,当 PDF 文件上传至 Amazon Simple Storage Service(Amazon S3)存储桶时,创建好的应用程序会自动加密 PDF 文件。要实现此应用程序,您要创建以下资源:

  • S3 存储桶,供用户上传 PDF 文件

  • Python 中的 Lambda 函数,用于读取上传的文件并创建加密的、受密码保护的文件版本

  • 第二个 S3 存储桶,供 Lambda 保存加密文件

您还可以创建一个 AWS Identity and Access Management(IAM)策略,来授予 Lambda 函数对 S3 存储桶执行读写操作的权限。

该示意图显示了 S3 存储桶、Lambda 函数和另一 S3 存储桶之间的数据流
提示

若为 Lambda 全新用户,建议先阅读教程创建第一个 Lambda 函数,再创建此示例应用程序。

您可以使用 AWS Management Console 或 AWS Command Line Interface(AWS CLI)创建并配置资源来手动部署应用程序。您也可以使用 AWS Serverless Application Model(AWS SAM)来部署应用程序。AWS SAM 是基础设施即代码(IaC)工具。若借助 IaC,则不必手动创建资源,只需在代码中定义资源,就能自动部署这些资源。

若想在部署此示例应用程序之前,了解有关将 Lambda 与 IaC 结合使用的更多信息,请参阅将 Lambda 与基础设施即代码(IaC)结合使用

先决条件

在创建示例应用程序之前,确保已安装好所需的命令行工具。

  • AWS CLI

    您可以使用 AWS Management Console 或 AWS CLI 手动部署应用程序的资源。要使用 CLI,请按照《AWS Command Line Interface User Guide》中的安装说明进行安装。

  • AWS SAM CLI

    若要使用 AWS SAM 部署示例应用程序,则需要同时安装 AWS CLI 和 AWS SAM CLI。要安装 AWS SAM CLI,请按照《AWS SAM User Guide》中的安装说明进行安装。

  • pytest 模块

    部署应用程序后,您可以使用我们提供的自动化 Python 测试脚本对其进行测试。要使用此脚本,请运行以下命令,将 pytest 程序包安装到本地开发环境中:

    pip install pytest

要使用 AWS SAM 部署应用程序,还必须在生成计算机上安装 Docker

下载示例应用程序文件

要创建并测试示例应用程序,请在项目目录中创建以下文件:

  • lambda_function.py – 执行文件加密的 Lambda 函数的 Python 函数代码

  • requirements.txt – 定义 Python 函数代码所需的依赖项的清单文件

  • template.yaml – 可用于部署应用程序的 AWS SAM 模板

  • test_pdf_encrypt.py – 可用于自动测试应用程序的测试脚本

  • pytest.ini – 测试脚本的配置文件

展开以下各部分,查看代码,继而详细了解每个文件在创建和测试应用程序中所起的作用。要在本地计算机上创建文件,请复制并粘贴以下代码,或从 aws-lambda-developer-guideGitHub 存储库下载文件。

将以下代码复制并粘贴到名为 lambda_function.py 的文件。

from pypdf import PdfReader, PdfWriter import uuid import os from urllib.parse import unquote_plus import boto3 # Create the S3 client to download and upload objects from S3 s3_client = boto3.client('s3') def lambda_handler(event, context): # Iterate over the S3 event object and get the key for all uploaded files for record in event['Records']: bucket = record['s3']['bucket']['name'] key = unquote_plus(record['s3']['object']['key']) # Decode the S3 object key to remove any URL-encoded characters download_path = f'/tmp/{uuid.uuid4()}.pdf' # Create a path in the Lambda tmp directory to save the file to upload_path = f'/tmp/converted-{uuid.uuid4()}.pdf' # Create another path to save the encrypted file to # If the file is a PDF, encrypt it and upload it to the destination S3 bucket if key.lower().endswith('.pdf'): s3_client.download_file(bucket, key, download_path) encrypt_pdf(download_path, upload_path) encrypted_key = add_encrypted_suffix(key) s3_client.upload_file(upload_path, f'{bucket}-encrypted', encrypted_key) # Define the function to encrypt the PDF file with a password def encrypt_pdf(file_path, encrypted_file_path): reader = PdfReader(file_path) writer = PdfWriter() for page in reader.pages: writer.add_page(page) # Add a password to the new PDF writer.encrypt("my-secret-password") # Save the new PDF to a file with open(encrypted_file_path, "wb") as file: writer.write(file) # Define a function to add a suffix to the original filename after encryption def add_encrypted_suffix(original_key): filename, extension = original_key.rsplit('.', 1) return f'{filename}_encrypted.{extension}'
注意

在此示例代码中,加密文件 (my-secret-password) 的密码被硬编码到函数代码中。在生产应用程序中,切勿在函数代码中包含密码等敏感信息。使用 AWS Secrets Manager 安全地存储敏感参数。

Python 函数代码包含三个函数:一个是 Lambda 在调用函数时运行的处理程序函数,以及两个名为 add_encrypted_suffixencrypt_pdf 的独立函数,处理程序调用它们来执行 PDF 加密。

在 Amazon S3 调用函数时,Lambda 会将一个 JSON 格式的事件参数传递给函数,该参数包含有关导致调用的事件详细信息。在本例中,此类信息包括 S3 存储桶的名称和上传文件的对象键。要了解有关 Amazon S3 事件对象格式的更多信息,请参阅使用 Lambda 处理 Amazon S3 事件通知

然后,函数使用 AWS SDK for Python (Boto3) 将事件对象中指定的 PDF 文件下载到其本地临时存储目录,再使用 pypdf 库对文件进行加密。

最后,函数使用 Boto3 SDK 将加密文件存储在 S3 目标存储桶中。

将以下代码复制并粘贴到名为 requirements.txt 的文件。

boto3 pypdf

在本示例中,函数代码只有两个不属于标准 Python 库的依赖项:一是适用于 Python 的 SDK(Boto3),二是函数用来执行 PDF 加密的 pypdf 程序包。

注意

适用于 Python 的 SDK(Boto3)的一个版本作为 Lambda 运行时的一部分包含在内,因此无需将 Boto3 添加到函数的部署包中,代码即可运行。不过,为了完全控制函数依赖项并避免可能出现的版本不一致问题,Python 的最佳实践是将所有函数依赖项包含在函数的部署包中。请参阅Python 中的运行时系统依赖项,了解更多信息。

将以下代码复制并粘贴到名为 template.yaml 的文件。

AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Resources: EncryptPDFFunction: Type: AWS::Serverless::Function Properties: FunctionName: EncryptPDF Architectures: [x86_64] CodeUri: ./ Handler: lambda_function.lambda_handler Runtime: python3.12 Timeout: 15 MemorySize: 256 LoggingConfig: LogFormat: JSON Policies: - AmazonS3FullAccess Events: S3Event: Type: S3 Properties: Bucket: !Ref PDFSourceBucket Events: s3:ObjectCreated:* PDFSourceBucket: Type: AWS::S3::Bucket Properties: BucketName: EXAMPLE-BUCKET EncryptedPDFBucket: Type: AWS::S3::Bucket Properties: BucketName: EXAMPLE-BUCKET-encrypted

AWS SAM 模板定义了您为应用程序创建的资源。在本示例中,模板使用 AWS::Serverless::Function 类型定义一个 Lambda 函数,并使用 AWS::S3::Bucket 类型定义两个 S3 存储桶。模板中指定的存储桶名称是占位符。在使用 AWS SAM 部署应用程序之前,您需要编辑模板,使用符合 S3 存储桶命名规则的全局唯一名称重命名存储桶。使用 AWS SAM 来部署资源 中将进一步介绍该步骤。

Lambda 函数资源的定义使用 S3Event 事件属性为函数配置触发器。只要在源存储桶中创建对象,该触发器就会调用函数。

函数定义还指定了要附加到函数执行角色的 AWS Identity and Access Management(IAM)策略。AWS 托管式策略 AmazonS3FullAccess 授予函数在 Amazon S3 中读取和写入对象所需的权限。

将以下代码复制并粘贴到名为 test_pdf_encrypt.py 的文件。

import boto3 import json import pytest import time import os @pytest.fixture def lambda_client(): return boto3.client('lambda') @pytest.fixture def s3_client(): return boto3.client('s3') @pytest.fixture def logs_client(): return boto3.client('logs') @pytest.fixture(scope='session') def cleanup(): # Create a new S3 client for cleanup s3_client = boto3.client('s3') yield # Cleanup code will be executed after all tests have finished # Delete test.pdf from the source bucket source_bucket = 'EXAMPLE-BUCKET' source_file_key = 'test.pdf' s3_client.delete_object(Bucket=source_bucket, Key=source_file_key) print(f"\nDeleted {source_file_key} from {source_bucket}") # Delete test_encrypted.pdf from the destination bucket destination_bucket = 'EXAMPLE-BUCKET-encrypted' destination_file_key = 'test_encrypted.pdf' s3_client.delete_object(Bucket=destination_bucket, Key=destination_file_key) print(f"Deleted {destination_file_key} from {destination_bucket}") @pytest.mark.order(1) def test_source_bucket_available(s3_client): s3_bucket_name = 'EXAMPLE-BUCKET' file_name = 'test.pdf' file_path = os.path.join(os.path.dirname(__file__), file_name) file_uploaded = False try: s3_client.upload_file(file_path, s3_bucket_name, file_name) file_uploaded = True except: print("Error: couldn't upload file") assert file_uploaded, "Could not upload file to S3 bucket" @pytest.mark.order(2) def test_lambda_invoked(logs_client): # Wait for a few seconds to make sure the logs are available time.sleep(5) # Get the latest log stream for the specified log group log_streams = logs_client.describe_log_streams( logGroupName='/aws/lambda/EncryptPDF', orderBy='LastEventTime', descending=True, limit=1 ) latest_log_stream_name = log_streams['logStreams'][0]['logStreamName'] # Retrieve the log events from the latest log stream log_events = logs_client.get_log_events( logGroupName='/aws/lambda/EncryptPDF', logStreamName=latest_log_stream_name ) success_found = False for event in log_events['events']: message = json.loads(event['message']) status = message.get('record', {}).get('status') if status == 'success': success_found = True break assert success_found, "Lambda function execution did not report 'success' status in logs." @pytest.mark.order(3) def test_encrypted_file_in_bucket(s3_client): # Specify the destination S3 bucket and the expected converted file key destination_bucket = 'EXAMPLE-BUCKET-encrypted' converted_file_key = 'test_encrypted.pdf' try: # Attempt to retrieve the metadata of the converted file from the destination S3 bucket s3_client.head_object(Bucket=destination_bucket, Key=converted_file_key) except s3_client.exceptions.ClientError as e: # If the file is not found, the test will fail pytest.fail(f"Converted file '{converted_file_key}' not found in the destination bucket: {str(e)}") def test_cleanup(cleanup): # This test uses the cleanup fixture and will be executed last pass

自动化测试脚本将执行三个测试函数来确认应用程序运行是否正确:

  • 测试 test_source_bucket_available 通过将测试 PDF 文件上传到存储桶来确认源存储桶是否已成功创建。

  • 测试 test_lambda_invoked 通过询问函数的最新 CloudWatch Logs 日志流,来确认上传测试文件时,Lambda 函数是否已运行并报告成功。

  • 测试 test_encrypted_file_in_bucket 确认目标存储桶是否包含加密 test_encrypted.pdf 文件。

待这些测试运行完毕后,脚本会执行额外的清理步骤,以便从源存储桶和目标存储桶中删除 test.pdftest_encrypted.pdf 文件。

与 AWS SAM 模板一样,此文件中指定的存储桶名称是占位符。在运行测试前,必须使用应用程序的真实存储桶名称编辑此文件。使用自动化脚本测试应用程序 中将进一步介绍此步骤

将以下代码复制并粘贴到名为 pytest.ini 的文件。

[pytest] markers = order: specify test execution order

这是为了指定 test_pdf_encrypt.py 脚本中测试运行的顺序。

部署应用程序

您可以手动或使用 AWS SAM 自动创建并部署此示例应用程序的资源。在生产环境中,建议使用 AWS SAM 之类的 IaC 工具来快速、可重复地部署整个无服务器应用程序,无需采用手动流程。

在本示例中,可按照控制台或 AWS CLI 说明了解如何单独配置每种 AWS 资源,也可以直接跳至 使用 AWS SAM 来部署资源,使用几个 CLI 命令快速部署应用程序。

手动部署资源

要手动部署应用程序,请执行以下步骤:

  • 创建源和目标 Amazon S3 存储桶

  • 创建一个 Lambda 函数,用于加密 PDF 文件并将加密版文件保存到 S3 存储桶

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

按照以下段落中的说明创建并配置资源。

创建两个 S3 存储桶

先创建两个 S3 存储桶。第一个是源存储桶,供您向其上传 PDF 文件。第二个是目标存储桶,供 Lambda 保存调用函数时加密的文件。

Console
创建 S3 存储桶(控制台)
  1. 打开 Amazon S3 控制台的存储桶页面。

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

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

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

    2. 对于 AWS 区域,请选择最接近您地理位置的 AWS 区域。在部署过程的后面部分,您必须在同个 AWS 区域 中创建 Lambda 函数,因此请记下您选择的区域。

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

  5. 重复步骤 1 到 4 以创建自己的目标存储桶。在存储桶名称中输入 SOURCEBUCKET-encrypted,其中 SOURCEBUCKET 是您刚刚创建的源存储桶的名称。

AWS CLI
创建 Amazon S3 存储桶(AWS CLI)
  1. 运行以下 CLI 命令来创建自己的源存储桶。您为存储桶选择的名称必须具有全局唯一性,并遵守 Amazon S3 存储桶命名规则。名称只能由小写字母、数字、句点(.)和连字符(-)组成。对于 regionLocationConstraint,请选择最接近您地理位置的 AWS 区域

    aws s3api create-bucket --bucket SOURCEBUCKET --region us-west-2 \ --create-bucket-configuration LocationConstraint=us-west-2

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

  2. 运行以下命令来创建自己的目标存储桶。对于存储桶名称,必须使用 SOURCEBUCKET-encrypted,其中 SOURCEBUCKET 是您在步骤 1 中创建的源存储桶名称。对于 regionLocationConstraint,请选择与用于创建源存储桶时相同的 AWS 区域。

    aws s3api create-bucket --bucket SOURCEBUCKET-encrypted --region us-west-2 \ --create-bucket-configuration LocationConstraint=us-west-2

创建执行角色(仅限 AWS CLI)

执行角色是一个 IAM 角色,用于向 Lambda 函数授予访问 AWS 服务 和资源的权限。使用 Lambda 控制台创建函数时,Lambda 会自动创建一个执行角色。只有选择使用 AWS CLI 来部署应用程序时,才需要手动创建角色。要授予函数对 Amazon S3 的读取和写入权限,必须附加 AWS 托管式策略 AmazonS3FullAccess

Console

只有选择使用 AWS CLI 来部署应用程序时,才需要执行此步骤。

AWS CLI
创建执行角色并附加 AmazonS3FullAccess 托管式策略(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" } ] }
  2. 在保存 JSON 信任策略文档的目录中,运行以下 CLI 命令来创建执行角色。

    aws iam create-role --role-name LambdaS3Role --assume-role-policy-document file://trust-policy.json
  3. 要附加 AmazonS3FullAccess 托管式策略,请运行下列 CLI 命令。

    aws iam attach-role-policy --role-name LambdaS3Role --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess

创建函数部署包

要创建函数,您需要创建包含函数代码和所有依赖项的部署包。对于此应用程序,函数代码使用单独的库来加密 PDF。

创建部署包
  1. 导航到包含之前创建的或从 GitHub 下载的 lambda_function.pyrequirements.txt 文件的项目目录,然后创建一个名为 package 的新目录。

  2. 运行以下命令,将 requirements.txt 文件中指定的依赖项安装到 package 目录中。

    pip install -r requirements.txt --target ./package/
  3. 创建一个包含应用程序代码及其依赖项的 .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 函数

创建 Lambda 函数

现在,您可以使用在上一步中创建的部署包来部署 Lambda 函数。

Console
创建函数(控制台)

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

为确保加密大型 PDF 文件时函数不会超时,必须配置该函数的内存和超时设置。您还要将函数的日志格式设置为 JSON。使用提供的测试脚本时,必须配置 JSON 格式的日志,以便其可以从 CloudWatch Logs 中读取函数的调用状态,从而确认是否成功调用。

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

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

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

  4. 选择从头开始创作

  5. 基本信息中,执行以下操作:

    1. 对于 Function name(函数名称),请输入 EncryptPDF

    2. 对于运行时,选择 Python 3.12

    3. 对于架构,选择 x86_64

  6. 选择 Create function (创建函数)

上传函数代码(控制台)
  1. 代码源窗格中,选择上传自

  2. 选择 .zip 文件

  3. 选择上传

  4. 在文件选择器中,选择 .zip 文件,然后选择打开

  5. 选择保存

配置函数内存和超时(控制台)
  1. 选择函数的配置选项卡。

  2. 常规配置窗格中,选择编辑

  3. 内存设置为 256 MB,并将超时设置为 15 秒。

  4. 选择保存

配置日志格式(控制台)
  1. 选择函数的配置选项卡。

  2. 选择监控和操作工具

  3. 日志记录配置窗格中,选择编辑

  4. 对于日志记录配置,选择 JSON

  5. 选择保存

AWS CLI
创建函数(AWS CLI)
  • 从包含 lambda_function.zip 文件的目录中运行以下命令。对于 region 参数,将 us-west-2 替换为创建 S3 存储桶时使用的区域。

    aws lambda create-function --function-name EncryptPDF \ --zip-file fileb://lambda_function.zip --handler lambda_function.lambda_handler \ --runtime python3.12 --timeout 15 --memory-size 256 \ --role arn:aws:iam::123456789012:role/LambdaS3Role --region us-west-2 \ --logging-config LogFormat=JSON

配置 Amazon S3 触发器来调用函数

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

重要

此程序将 S3 存储桶配置为每次在该存储桶中创建对象时调用您的函数。请确保仅在源存储桶上配置。如果您的 Lambda 函数在调用此函数的同一个存储桶中创建对象,则可以在循环中持续调用您的函数。这可能会导致您的 AWS 账户 产生额外费用。

Console
配置 Amazon S3 触发器(控制台)
  1. 打开 Lambda 控制台的函数页面,然后选择函数 (EncryptPDF)。

  2. 选择添加触发器

  3. 选择 S3

  4. 存储桶下,选择自己的源存储桶。

  5. 事件类型下,选择所有对象创建事件

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

  7. 选择添加

    在您使用 Lambda 控制台创建触发器时,Lambda 会自动创建基于资源的策略,授予您选择的服务调用函数的权限。

AWS CLI
配置 Amazon S3 触发器(AWS CLI)
  1. 为了在添加文件时让 Amazon S3 源存储桶能调用函数,您首先需要使用基于资源的策略为函数配置权限。基于资源的策略声明授予其他 AWS 服务 调用您函数的权限。要授予 Amazon S3 调用函数的权限,请运行以下 CLI 命令。请务必将 source-account 参数替换为您的 AWS 账户 ID 并使用自己的源存储桶名称。

    aws lambda add-permission --function-name EncryptPDF \ --principal s3.amazonaws.com --statement-id s3invoke --action "lambda:InvokeFunction" \ --source-arn arn:aws:s3:::SOURCEBUCKET \ --source-account 123456789012

    您使用此命令定义的策略允许 Amazon S3 仅在源存储桶上执行操作时调用函数。

    注意

    虽然 S3 存储桶名称具有全局唯一性,但在使用基于资源的策略时,最佳做法是指定存储桶必须属于您的账户。这是因为,如果删除一个存储桶,则另一个 AWS 账户 账户可能会创建具有相同 Amazon 资源名称(ARN)的存储桶。

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

    { "LambdaFunctionConfigurations": [ { "Id": "EncryptPDFEventConfiguration", "LambdaFunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:EncryptPDF", "Events": [ "s3:ObjectCreated:Put" ] } ] }
  3. 运行以下 CLI 命令,将您创建的 JSON 文件中的通知设置应用到源存储桶。将 SOURCEBUCKET 替换为源存储桶的名称。

    aws s3api put-bucket-notification-configuration --bucket SOURCEBUCKET \ --notification-configuration file://notification.json

    要了解有关 put-bucket-notification-configuration 命令和 notification-configuration 选项的更多信息,请参阅 AWS CLI 命令参考中的 put-bucket-notification-configuration

使用 AWS SAM 来部署资源

要使用 AWS SAM CLI 来部署示例应用程序,请执行以下步骤。

请确保已安装最新版本的 CLI,并且生成计算机上安装了 Docker

  1. 编辑 template.yaml 文件来指定 S3 存储桶的名称。S3 存储桶必须具有符合 S3 存储桶命名规则的全局唯一名称。

    将存储桶名称 EXAMPLE-BUCKET 替换为所选名称,该名称必须由小写字母、数字、点(.)和连字符(-)组成。对于目标存储桶,将 EXAMPLE-BUCKET-encrypted 替换为 <source-bucket-name>-encrypted,其中 <source-bucket> 是您为源存储桶选择的名称。

  2. 在保存 template.yamllambda_function.pyrequirements.txt 文件的目录中,运行以下命令。

    sam build --use-container

    此命令会收集应用程序的构建构件,并将其以适当的格式放置在适当的位置进行部署。指定 --use-container 选项会在类似 Lambda 的 Docker 容器中构建函数。我们在这里使用它,您无需在本地计算机上安装 Python 3.12 即可进行构建。

    在构建过程中,AWS SAM 会在以模板中的 CodeUri 属性指定的位置中查找 Lambda 函数代码。在本例中,我们将当前目录指定为位置(./)。

    如果存在 requirements.txt 文件,则 AWS SAM 使用该文件来收集指定的依赖项。默认情况下,AWS SAM 会创建包含函数代码和依赖项的 .zip 部署包。您也可以选择使用 PackageType 属性将函数部署为容器映像。

  3. 要部署应用程序并创建 AWS SAM 模板中指定的 Lambda 和 Amazon S3 资源,请运行以下命令。

    sam deploy --guided

    使用 --guided 标志意味着 AWS SAM 将向您显示提示,以指导您完成部署过程。对于此部署,请按 Enter 接受默认选项。

在部署过程中,AWS SAM 将在 AWS 账户 中创建以下资源:

  • 一个名为 sam-app 的 AWS CloudFormation 堆栈

  • 一个名为 EncryptPDF 的 Lambda 函数

  • 两个 S3 存储桶,其名称为编辑 template.yaml AWS SAM 模板文件时选择的名称

  • 一个函数的 IAM 执行角色,名称格式为 sam-app-EncryptPDFFunctionRole-2qGaapHFWOQ8

AWS SAM 创建完资源后,您将看到以下消息:

Successfully created/updated stack - sam-app in us-west-2

测试应用程序

要测试应用程序,必须将 PDF 文件上传到源存储桶,还要确认 Lambda 已在目标存储桶中创建了加密版文件。在本示例中,您可以使用控制台或 AWS CLI 对此进行手动测试,也可以使用提供的测试脚本进行自动测试。

对于生产应用程序,可以使用单元测试等传统的测试方法和技术,来确认 Lambda 函数代码是否正常运行。最佳实践也是执行与所提供测试脚本中的测试类似的测试,这些测试使用真实的基于云的资源执行集成测试。在云端中执行集成测试可确认基础架构是否已正确部署,并确认事件是否按预期在不同的服务之间流动。要了解更多信息,请参阅 如何测试无服务器函数和应用程序

手动测试应用程序

您可以将 PDF 文件添加到 Amazon S3 源存储桶来手动测试函数。将文件添加到源存储桶时,应自动调用 Lambda 函数,并应在目标存储桶中存储加密版文件。

Console
通过上传文件来测试应用程序(控制台)
  1. 要将 PDF 文件上传到 S3 存储桶,请执行以下操作:

    1. 打开 Amazon S3 控制台的存储桶页面,然后选择您的源存储桶。

    2. 选择上传

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

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

  2. 执行以下操作,验证 Lambda 是否已将加密版 PDF 文件保存在目标存储桶中:

    1. 导航回 Amazon S3 控制台的存储桶页面,然后选择您的目标存储桶。

    2. 对象窗格中,现在应该可以到一个名称格式为 filename_encrypted.pdf 的文件(其中 filename.pdf 是已上传到源存储桶的文件的名称)。要下载加密的 PDF,请选择所需文件,然后选择下载

    3. 确认是否可以使用 Lambda 函数保护的密码 (my-secret-password) 打开下载的文件。

AWS CLI
通过上传文件来测试应用程序(AWS CLI)
  1. 在包含要上传的 PDF 文件的目录中,运行以下 CLI 命令。将 --bucket 参数替换为源存储桶的名称。对于 --key--body 参数,请使用测试文件的文件名。

    aws s3api put-object --bucket SOURCEBUCKET --key test.pdf --body ./test.pdf
  2. 验证函数是否已创建加密版文件并已保存到目标 S3 存储桶中。运行以下 CLI 命令,将 SOURCEBUCKET-encrypted 替换为自己的目标存储桶的名称。

    aws s3api list-objects-v2 --bucket SOURCEBUCKET-encrypted

    如果函数成功运行,您将看到类似于以下内容的输出。目标存储桶应包含名称格式为 <your_test_file>_encrypted.pdf 的文件,其中 <your_test_file> 是已上传文件的名称。

    { "Contents": [ { "Key": "test_encrypted.pdf", "LastModified": "2023-06-07T00:15:50+00:00", "ETag": "\"7781a43e765a8301713f533d70968a1e\"", "Size": 2763, "StorageClass": "STANDARD" } ] }
  3. 要下载 Lambda 保存在目标存储桶中的文件,请运行以下 CLI 命令。将 --bucket 参数替换为目标存储桶的名称。对于 --key 参数,请使用文件名 <your_test_file>_encrypted.pdf,其中 <your_test_file> 是已上传的测试文件的名称。

    aws s3api get-object --bucket SOURCEBUCKET-encrypted --key test_encrypted.pdf my_encrypted_file.pdf

    该命令会将文件下载到当前目录并将其另存为 my_encrypted_file.pdf

  4. 确认是否可以使用 Lambda 函数保护的密码 (my-secret-password) 打开下载的文件。

使用自动化脚本测试应用程序

要使用提供的测试脚本测试应用程序,请先确保已在本地环境中安装了 pytest 模块。可以通过运行以下命令安装 pytest

pip install pytest

您还需要编辑 test_pdf_encrypt.py 文件中的代码,将以占位符表示的存储桶名称分别替换为 Amazon S3 源存储桶和目标存储桶的名称。对 test_pdf_encrypt.py 进行以下更改:

  • test_source_bucket_available 函数中,将 EXAMPLE-BUCKET 替换为源存储桶的名称。

  • test_encrypted_file_in_bucket 函数中,将 EXAMPLE-BUCKET-encrypted 替换为 <source-bucket>-encrypted,其中 <source-bucket> 是源存储桶的名称。

  • cleanup 函数中,将 EXAMPLE-BUCKET 替换为源存储桶的名称,并将 EXAMPLE-BUCKET-encrypted 替换为 ≪source-bucket>-encrypted,其中 <source-bucket> 是源存储桶的名称。

要运行测试,请执行以下操作:

  • 将名为 test.pdf 的 PDF 文件保存在包含 test_pdf_encrypt.pypytest.ini 文件的目录中。

  • 打开终端或 Shell 程序,从包含测试文件的目录中运行以下命令。

    pytest -s -v

测试完成后,输出应与以下内容类似:

============================================================== test session starts ========================================================= platform linux -- Python 3.12.2, pytest-7.2.2, pluggy-1.0.0 -- /usr/bin/python3 cachedir: .pytest_cache hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/pdf_encrypt_app/.hypothesis/examples') Test order randomisation NOT enabled. Enable with --random-order or --random-order-bucket=<bucket_type> rootdir: /home/pdf_encrypt_app, configfile: pytest.ini plugins: anyio-3.7.1, hypothesis-6.70.0, localserver-0.7.1, random-order-1.1.0 collected 4 items test_pdf_encrypt.py::test_source_bucket_available PASSED test_pdf_encrypt.py::test_lambda_invoked PASSED test_pdf_encrypt.py::test_encrypted_file_in_bucket PASSED test_pdf_encrypt.py::test_cleanup PASSED Deleted test.pdf from EXAMPLE-BUCKET Deleted test_encrypted.pdf from EXAMPLE-BUCKET-encrypted =============================================================== 4 passed in 7.32s ==========================================================

后续步骤

您已经创建了示例应用程序,现在可以在所提供代码的基础上创建其他类型的文件处理应用程序。修改 lambda_function.py 文件中的代码,为用例实现文件处理逻辑。

许多常见的文件处理用例都涉及图像处理。使用 Python 时,pillow 等常用的图像处理库通常包含 C 或 C++ 组件。为了确保函数的部署包与 Lambda 执行环境兼容,请务必使用正确的源分发二进制文件。

使用 AWS SAM 来部署资源时,必须额外采取一些步骤,以便在部署包中包含正确的源分发。由于 AWS SAM 不会为与生成计算机不同的平台安装依赖项,因此,如果生成机器使用与 Lambda 执行环境不同的操作系统或架构,则在 requirements.txt 文件中指定正确的源分发(.whl 文件)将不适用。所以,应执行以下操作之一: