

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

# 使用 Lambda 以无服务器方式处理文件
<a name="tutorial-process-files-with-lambda"></a>

文件处理工作流程通常从到达 NFS 或 SMB 文件共享的文件开始，这些文件包括来自分支机构的扫描文档、现场团队上传的图像、从联络中心捕获的音频或合作伙伴提供的数据文件。

通过连接到 FSx for ONTAP 卷的 Amazon S3 接入点， AWS Lambda 函数可以直接使用 Amazon S3 API 读取和写入文件。 File-level 可以针对您的用户和应用程序通过 NFS 和 SMB 访问的相同数据进行无服务器处理操作。

本教程显示了三种常见的文件处理模式。每个示例都通过接入点从卷中读取文件，使用 AWS 服务或库对其进行处理，然后将结果写回卷。


| 示例 | Input | Processing | Output | 
| --- | --- | --- | --- | 
| [示例 1：生成图像缩略图](#tutorial-lambda-thumbnail) | JPEG 图片 | 枕头（图片库） | 调整了缩略图大小 | 
| [示例 2：从文档中提取文本](#tutorial-lambda-textract) | PDF 文档 | Amazon Textract | 提取的文本 (JSON) | 
| [示例 3：转录音频文件](#tutorial-lambda-transcribe) | MP3 音频 | Amazon Transcribe | 笔录 (JSON) | 

**注意**  
完成本教程大约需要 **40 到 60 分钟**。 AWS 服务 使用者会对您创建的资源产生费用。如果您及时完成所有步骤，包括**清理**部分，则美国东部（弗吉尼亚北部）的预期费用将低于 **1美元** AWS 区域。该估算值不包括 FSx 对 ONTAP 容量本身的持续费用。

## 先决条件
<a name="tutorial-lambda-prerequisites"></a>

在开始之前，请确保您具有以下各项：
+ 连接了 Amazon S3 接入点的 ONTAP 卷的 FSx。有关创建接入点的说明，请参阅[创建接入点](fsxn-creating-access-points.md)。
+ 您的接入点的接入点别名。你可以在 Amazon FSx 控制台中找到它，也可以通过运行来找到。`aws fsx describe-s3-access-point-attachments`
+ AWS CLI 已安装并配置版本 1 或版本 2。本教程中的`aws lambda invoke`命令包括`--cli-binary-format raw-in-base64-out`选项，该选项在 AWS CLI 版本 2 中是必需的，这样原始 JSON 负载就不会被解释为 base64。如果您使用 AWS CLI 版本 1，请省略该选项。
+ 调用者（运行本教程的用户或角色）的 IAM 权限，用于调用 Lambda 函数 (`lambda:CreateFunction`,`lambda:InvokeFunction`)、访问 Amazon S3 接入点 (`s3:GetObject`,`s3:PutObject`) 和传递 Lambda 执行角色 ()。`iam:PassRole`

**注意**  
本教程使用默认 Lambda 配置，其中函数在您的 VPC 之外的托管网络中运行。在这种情况下，接入点必须具有**互联网**网络来源，以便功能可以访问它。如果您将 Lambda 函数附加到 VPC，则可以在接入点上使用 VPC 网络来源；VPC 必须具有 Amazon S3 网关或接口终端节点。有关更多信息，请参阅 [为 Amazon S3 接入点配置网络访问权限](configuring-network-access-for-s3-access-points.md)。

## 步骤 1：上传示例文件
<a name="tutorial-lambda-upload-samples"></a>

下载以下示例文件并通过接入点将其上传到您的 FSx for ONTAP 卷。在本教程中`{{my-ap-alias-ext-s3alias}}`，请使用您的接入点别名替换。
+ **示例图片：**下载 [NASA Blue Marble 图片](https://eoimages.gsfc.nasa.gov/images/imagerecords/73000/73909/world.topo.bathy.200412.3x5400x2700.jpg)（公共领域，2.4 MB）并将其另存为`sample-image.jpg`。
+ **示例音频：**从 [Amazon Transcribe 入门教程](https://docs.aws.amazon.com/hands-on/latest/create-audio-transcript-transcribe/create-audio-transcript-transcribe.html) (410 KB) 中下载[示例音频文件](https://d1.awsstatic.com/tmt/create-audio-transcript-transcribe/transcribe-sample.5fc2109bb28268d10fbc677e64b7e59256783d3c.mp3)并将其另存为。`sample-audio.mp3`

通过接入点将示例文件上传到您的 FSx for ONTAP 卷。

```
$ aws s3 cp sample-image.jpg s3://{{my-ap-alias-ext-s3alias}}/samples/images/sample-image.jpg
aws s3 cp sample-audio.mp3 s3://{{my-ap-alias-ext-s3alias}}/samples/audio/sample-audio.mp3
```

**注意**  
样本图像是美国宇航局的蓝色大理石照片（公共领域，2.4 MB）。示例音频来自 [Amazon Transcribe 入门教程](https://docs.aws.amazon.com/hands-on/latest/create-audio-transcript-transcribe/create-audio-transcript-transcribe.html) (410 KB)。样本 PDF 是在中生成的[示例 2：从文档中提取文本](#tutorial-lambda-textract)。

## 步骤 2：创建 Lambda 执行角色
<a name="tutorial-lambda-create-role"></a>

Lambda 函数扮演执行角色与其他函数进行交互。 AWS 服务在本教程中，请附上 CloudWatch 日志记录的 AWS托管`AWSLambdaBasicExecutionRole`策略，然后添加一个内联策略，授予对 Amazon S3 接入点以及示例使用的 Textract 和 Transcribe API 的访问权限。

### 创建 Lambda 执行角色
<a name="tutorial-lambda-create-role-steps"></a>

用您的值替换`{{region}}``{{account-id}}`、和`{{access-point-name}}`。

1. 将以下信任策略另存为`trust-policy.json`。

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

1. 将以下内联权限策略另存为`permissions-policy.json`。它授予访问接入点和示例使用的其他服务的访问权限。

   ```
   {
       "Version": "2012-10-17", 		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": ["s3:GetObject", "s3:PutObject", "s3:ListBucket"],
               "Resource": [
                   "arn:aws:s3:{{region}}:{{account-id}}:accesspoint/{{access-point-name}}",
                   "arn:aws:s3:{{region}}:{{account-id}}:accesspoint/{{access-point-name}}/object/*"
               ]
           },
           {
               "Effect": "Allow",
               "Action": ["textract:DetectDocumentText"],
               "Resource": "*"
           },
           {
               "Effect": "Allow",
               "Action": [
                   "transcribe:StartTranscriptionJob",
                   "transcribe:GetTranscriptionJob"
               ],
               "Resource": "*"
           }
       ]
   }
   ```

1. 创建角色，附加托管日志策略并附加内联策略。

   ```
   $ aws iam create-role \
       --role-name {{fsxn-lambda-file-processor}} \
       --assume-role-policy-document file://trust-policy.json
   
   aws iam attach-role-policy \
       --role-name {{fsxn-lambda-file-processor}} \
       --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
   
   aws iam put-role-policy \
       --role-name {{fsxn-lambda-file-processor}} \
       --policy-name fsxn-access-point-policy \
       --policy-document file://permissions-policy.json
   ```

## 集成到您的工作流程中
<a name="tutorial-lambda-workflow-integration"></a>

本教程中的示例使用带有测试事件的手动调用。在生产环境中，您可以使用以下方法自动触发这些函数：
+ **亚马逊 EventBridge 日程安排。**定期运行该函数（例如，每小时或每天）以处理新文件。该功能可以通过接入点列出文件并处理任何尚未处理的文件。有关更多信息，请参阅*亚马逊 EventBridge用户*指南 EventBridge中的[使用计划 Lambda 函数](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-run-lambda-schedule.html)。
+ **亚马逊 API Gateway。**将该函数公开为 HTTP API，以便用户或应用程序可以根据需要请求处理特定文件。有关更多信息，请参阅*亚马逊 [API Gateway 开发者指南中的使用 Lambda 集成 API Gateway RES](https://docs.aws.amazon.com/apigateway/latest/developerguide/getting-started-with-lambda-integration.html) T API*。
+ **Step Functions。**编排组合多个 Lambda 函数的多步骤文件处理管道。例如，一种从文档中提取文本、对其进行翻译并将结果写回卷的工作流程。有关更多信息，请参阅*AWS Step Functions 开发人员*指南中的[使用 Step Functions 调用 Lambda](https://docs.aws.amazon.com/step-functions/latest/dg/connect-lambda.html)。

## 示例 1：生成图像缩略图
<a name="tutorial-lambda-thumbnail"></a>

此示例从 FSx 中读取 ONTAP 音量的 JPEG 图像，使用 Pillow 图像库将其大小调整为 200 像素的缩略图，然后将缩略图写回音量。

**Lambda 函数代码**

将以下代码另存为`lambda_function.py`。

```
import boto3
from io import BytesIO
from PIL import Image

s3 = boto3.client('s3')

def lambda_handler(event, context):
    bucket = event['access_point_alias']
    key = event['key']

    # Read the image from FSx through the access point
    response = s3.get_object(Bucket=bucket, Key=key)
    image_data = response['Body'].read()

    # Resize to thumbnail
    img = Image.open(BytesIO(image_data))
    img.thumbnail((200, 200))

    # Write the thumbnail back to FSx
    buffer = BytesIO()
    img.save(buffer, format='JPEG', quality=85)
    buffer.seek(0)

    thumbnail_key = key.rsplit('.', 1)[0] + '_thumbnail.jpg'
    s3.put_object(
        Bucket=bucket,
        Key=thumbnail_key,
        Body=buffer.getvalue(),
        ContentType='image/jpeg'
    )

    return {
        'original_size': len(image_data),
        'thumbnail_size': len(buffer.getvalue()),
        'thumbnail_key': thumbnail_key
    }
```

**创建并调用该函数**

此功能需要 Pillow 库。创建一个包含专为 Lambda Linux 运行时构建的 Pillow 的部署包。

```
$ # Create a deployment package with Pillow for Lambda (Linux)
mkdir package && pip install Pillow -t package/ \
    --platform manylinux2014_x86_64 --only-binary=:all:
cd package && zip -r ../thumbnail-function.zip .
cd .. && zip thumbnail-function.zip lambda_function.py

# Create the function
aws lambda create-function \
    --function-name {{fsxn-thumbnail-generator}} \
    --runtime python3.12 \
    --handler lambda_function.lambda_handler \
    --role arn:aws:iam::{{account-id}}:role/{{fsxn-lambda-file-processor}} \
    --zip-file fileb://thumbnail-function.zip \
    --timeout 30 \
    --memory-size 256

# Invoke with a test event
aws lambda invoke \
    --function-name {{fsxn-thumbnail-generator}} \
    --cli-binary-format raw-in-base64-out \
    --payload '{"access_point_alias": "{{my-ap-alias-ext-s3alias}}", "key": "samples/images/sample-image.jpg"}' \
    response.json

cat response.json
```

**验证结果**

```
$ aws s3 ls s3://{{my-ap-alias-ext-s3alias}}/samples/images/
2024-01-23 12:19:32    2566770 sample-image.jpg
2024-01-23 12:25:49       7065 sample-image_thumbnail.jpg
```

最初的 2.4 MB 图像（5400 × 2700 像素）的大小调整为 7 KB 的缩略图（200 × 100 像素）。

## 示例 2：从文档中提取文本
<a name="tutorial-lambda-textract"></a>

此示例从 FSx for ONTAP 卷中读取 PDF 文档，将其发送到 Amazon Textract 以提取文本，然后将提取的文本作为 JSON 文件写回该卷。

**创建并上传示例 PDF**

在本示例中，你需要在 FSx for ONTAP 卷上有一个 PDF 文档。以下 Python 脚本生成一个简单的发票 PDF 并通过接入点将其上传。在您的本地计算机上运行此脚本（不是 Lambda）。

```
$ pip install fpdf2 boto3
```

```
# create_invoice.py — run locally to generate and upload a sample PDF
from fpdf import FPDF
import boto3

pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", "B", 24)
pdf.cell(0, 15, "INVOICE", new_x="LMARGIN", new_y="NEXT", align="C")
pdf.set_font("Helvetica", "", 12)
pdf.cell(0, 8, "Invoice Number: INV-2024-00142", new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 8, "Date: January 15, 2024", new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 8, "Customer: Example Corp", new_x="LMARGIN", new_y="NEXT")
pdf.ln(5)
pdf.set_font("Helvetica", "B", 12)
pdf.cell(80, 8, "Description", border=1)
pdf.cell(30, 8, "Qty", border=1, align="C")
pdf.cell(40, 8, "Unit Price", border=1, align="R")
pdf.cell(40, 8, "Amount", border=1, align="R")
pdf.ln()
pdf.set_font("Helvetica", "", 12)
for desc, qty, price, amt in [
    ("Cloud Storage Service", "1", "$2,400.00", "$2,400.00"),
    ("Data Transfer (TB)", "5", "$90.00", "$450.00"),
    ("Technical Support", "1", "$500.00", "$500.00"),
]:
    pdf.cell(80, 8, desc, border=1)
    pdf.cell(30, 8, qty, border=1, align="C")
    pdf.cell(40, 8, price, border=1, align="R")
    pdf.cell(40, 8, amt, border=1, align="R")
    pdf.ln()

s3 = boto3.client('s3')
s3.put_object(
    Bucket='{{my-ap-alias-ext-s3alias}}',
    Key='samples/documents/invoice.pdf',
    Body=pdf.output(),
    ContentType='application/pdf'
)
print("Uploaded invoice.pdf")
```

```
$ python3 create_invoice.py
```

**Lambda 函数代码**

将以下代码另存为`lambda_function.py`。

```
import boto3
import json

s3 = boto3.client('s3')
textract = boto3.client('textract')

def lambda_handler(event, context):
    bucket = event['access_point_alias']
    key = event['key']

    # Read the PDF from FSx through the access point
    response = s3.get_object(Bucket=bucket, Key=key)
    document_bytes = response['Body'].read()

    # Extract text with Textract
    textract_response = textract.detect_document_text(
        Document={'Bytes': document_bytes}
    )

    lines = [
        block['Text']
        for block in textract_response['Blocks']
        if block['BlockType'] == 'LINE'
    ]

    # Write extracted text as JSON back to FSx
    result = {
        'source_file': key,
        'total_lines': len(lines),
        'extracted_text': lines
    }

    output_key = key.rsplit('.', 1)[0] + '_extracted.json'
    s3.put_object(
        Bucket=bucket,
        Key=output_key,
        Body=json.dumps(result, indent=2),
        ContentType='application/json'
    )

    return {
        'lines_extracted': len(lines),
        'output_key': output_key
    }
```

**创建并调用该函数**

```
$ zip textract-function.zip lambda_function.py

aws lambda create-function \
    --function-name {{fsxn-text-extractor}} \
    --runtime python3.12 \
    --handler lambda_function.lambda_handler \
    --role arn:aws:iam::{{account-id}}:role/{{fsxn-lambda-file-processor}} \
    --zip-file fileb://textract-function.zip \
    --timeout 30 \
    --memory-size 256

aws lambda invoke \
    --function-name {{fsxn-text-extractor}} \
    --cli-binary-format raw-in-base64-out \
    --payload '{"access_point_alias": "{{my-ap-alias-ext-s3alias}}", "key": "samples/documents/invoice.pdf"}' \
    response.json

cat response.json
```

输出示例：

```
{"lines_extracted": 22, "output_key": "samples/documents/invoice_extracted.json"}
```

## 示例 3：转录音频文件
<a name="tutorial-lambda-transcribe"></a>

此示例为存储在 FSx 上的 ONTAP 音量的音频文件启动 Amazon Transcribe 作业。Amazon Transcribe 使用媒体文件 URI 中的接入点别名直接从接入点读取音频文件。作业完成后，该函数会将脚本写回卷中。

**Lambda 函数代码**

将以下代码另存为`lambda_function.py`。

```
import boto3
import json
import time
import urllib.request

s3 = boto3.client('s3')
transcribe = boto3.client('transcribe')

def lambda_handler(event, context):
    bucket = event['access_point_alias']
    key = event['key']
    media_format = key.rsplit('.', 1)[-1]  # mp3, wav, etc.

    # Start a Transcribe job pointing to the file on FSx
    job_name = f"fsxn-{int(time.time())}"
    transcribe.start_transcription_job(
        TranscriptionJobName=job_name,
        Media={'MediaFileUri': f's3://{bucket}/{key}'},
        MediaFormat=media_format,
        LanguageCode='en-US'
    )

    # Wait for the job to complete
    while True:
        status = transcribe.get_transcription_job(
            TranscriptionJobName=job_name
        )
        state = status['TranscriptionJob']['TranscriptionJobStatus']
        if state in ('COMPLETED', 'FAILED'):
            break
        time.sleep(5)

    if state == 'FAILED':
        raise Exception(
            status['TranscriptionJob'].get('FailureReason', 'Unknown error')
        )

    # Download the transcript
    transcript_uri = status['TranscriptionJob']['Transcript']['TranscriptFileUri']
    with urllib.request.urlopen(transcript_uri) as resp:
        transcript_data = json.loads(resp.read())

    transcript_text = transcript_data['results']['transcripts'][0]['transcript']

    # Write the transcript back to FSx
    result = {
        'source_file': key,
        'job_name': job_name,
        'transcript': transcript_text
    }

    output_key = key.rsplit('.', 1)[0] + '_transcript.json'
    s3.put_object(
        Bucket=bucket,
        Key=output_key,
        Body=json.dumps(result, indent=2),
        ContentType='application/json'
    )

    return {
        'transcript_length': len(transcript_text),
        'output_key': output_key
    }
```

**创建并调用该函数**

```
$ zip transcribe-function.zip lambda_function.py

aws lambda create-function \
    --function-name {{fsxn-audio-transcriber}} \
    --runtime python3.12 \
    --handler lambda_function.lambda_handler \
    --role arn:aws:iam::{{account-id}}:role/{{fsxn-lambda-file-processor}} \
    --zip-file fileb://transcribe-function.zip \
    --timeout 120

aws lambda invoke \
    --function-name {{fsxn-audio-transcriber}} \
    --cli-binary-format raw-in-base64-out \
    --payload '{"access_point_alias": "{{my-ap-alias-ext-s3alias}}", "key": "samples/audio/sample-audio.mp3"}' \
    --cli-read-timeout 180 \
    response.json

cat response.json
```

**注意**  
Transcribe 作业通常需要 15 到 45 秒才能完成。为此，该函数的超时设置为 120 秒。

## 注意事项
<a name="tutorial-lambda-considerations"></a>
+ **默认配置需要互联网来源。**默认情况下，Lambda 从您的 VPC 之外的托管基础设施访问 Amazon S3，这需要一个源自互联网的接入点。如果您将 Lambda 函数附加到 VPC，则可以改用 VPC-origin 接入点。有关详细信息，请参阅先决条件。
+ **文件大小限制。**Lambda 函数的最大内存为 10 GB，最长执行时间为 15 分钟。对于大型文件，可以考虑使用范围读取（`GetObject`带`Range`标头）或流式传输响应。
+ **Textract 限制。**同步 `DetectDocumentText` API 接受最大 10 MB 和 1 页的文档。对于多页文档，请使用异步 `StartDocumentTextDetection` API。
+ **Transcribe 直接从接入点读取。**Amazon Transcribe 接受`MediaFileUri`参数 () `s3://{{ap-alias}}/{{key}}` 中的接入点别名。Lambda 函数不需要下载和重新上传音频文件。
+ **文件系统用户权限。**与接入点关联的文件系统用户必须具有输入文件的读取权限和输出目录的写入权限。

## 清理
<a name="tutorial-lambda-clean-up"></a>

为避免持续收费，请删除您在本教程中创建的资源。

```
$ # Delete Lambda functions
aws lambda delete-function --function-name {{fsxn-thumbnail-generator}}
aws lambda delete-function --function-name {{fsxn-text-extractor}}
aws lambda delete-function --function-name {{fsxn-audio-transcriber}}

# Delete the IAM role and policies
aws iam delete-role-policy \
    --role-name {{fsxn-lambda-file-processor}} \
    --policy-name fsxn-access-point-policy
aws iam detach-role-policy \
    --role-name {{fsxn-lambda-file-processor}} \
    --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam delete-role --role-name {{fsxn-lambda-file-processor}}

# Delete sample files from your FSx volume
aws s3 rm s3://{{my-ap-alias-ext-s3alias}}/samples/ --recursive
```