ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于 AWS CDK (Go) 构建 S3 上传触发 Rekognition 图像识别并写入 DynamoDB 的完整实战

基于 AWS CDK (Go) 构建 S3 上传触发 Rekognition 图像识别并写入 DynamoDB 的完整实战

【免费下载链接】aws-cdk-examples

Example projects using the AWS CDK

项目地址:https://gitcode.com/gh_mirrors/aw/aws-cdk-examples
点击查看免费下载

导读

本文以aws-cdk-examples仓库中的 go/rekognition-lambda-s3-trigger 示例为主线,系统讲解如何用 AWS CDK(Go 语言)搭建一条"对象存储 → 事件触发 → AI 图像识别 → 结果落库"的完整链路。读完本文,你将掌握 CDK 基础设施代码(S3、DynamoDB、Lambda、IAM、事件通知)与 Lambda 处理器(RekognitionDetectLabels调用、DynamoDB 写入)的完整实现,并能直接cdk deploy部署验证。

架构与工作原理

该示例在 AWS 账户中创建以下资源(源自 README 的 "How it works" 一节):

  • S3 存储桶:用于存放待识别的图片对象;
  • DynamoDB 表:存储 Rekognition 识别出的标签(labels)结果;
  • Lambda 函数:当新图片上传到 S3 时被自动触发,调用 AWS Rekognition 执行图像分类(标签识别);
  • 角色与策略(Roles and Policies):为上述资源之间的访问授予最小必要权限;
  • Rekognition 标签结果:被写入 DynamoDB 表。

整体数据流为:开发者通过 AWS CLI 或 SDK 向 S3 上传.jpg/.jpeg/.png图片 → S3 产生OBJECT_CREATED事件通知 → Lambda 被触发 → Lambda 用 AWS SDK 调用 RekognitionDetectLabels识别图片中的物体/场景标签 → 将"图片名 + 标签列表"写入 DynamoDB 表。

基础设施代码解析:CDK Go 栈

核心栈定义位于 rekognition-lambda-s3-trigger.go,入口main()通过awscdk.NewApp创建应用并合成RekognitionLambdaS3TriggerStack。env()函数返回nil,表示按默认账户/区域部署(由环境变量或本地配置决定)。

1. 创建 S3 存储桶

// Create S3 Bucket bucket := awss3.NewBucket(stack, jsii.String("Bucket"), &awss3.BucketProps{ RemovalPolicy: awscdk.RemovalPolicy_DESTROY, })
  • RemovalPolicy_DESTROY表示删除栈(cdk destroy)时一并删除桶,适合示例/开发环境;生产环境建议改为RETAIN防止误删数据。

2. 创建 DynamoDB 表

// Create DynamoDb table table := awsdynamodb.NewTable(stack, jsii.String("Classifications"), &awsdynamodb.TableProps{ PartitionKey: &awsdynamodb.Attribute{ Name: jsii.String("image_name"), Type: awsdynamodb.AttributeType_STRING, }, RemovalPolicy: awscdk.RemovalPolicy_DESTROY, })
  • 表逻辑 ID 为Classifications,分区键image_name(字符串类型),对应每个 S3 对象的 key;
  • 默认按需计费(on-demand)计费模式,未显式配置BillingMode时使用默认值,对于低频演示场景足够。

3. 创建 Go Lambda 函数(含本地打包)

//Create Lambda function function := awscdklambdagoalpha.NewGoFunction(stack, jsii.String("main"), &awscdklambdagoalpha.GoFunctionProps{ Runtime: awslambda.Runtime_PROVIDED_AL2023(), Entry: jsii.String("./lambda-handler"), Bundling: &awscdklambdagoalpha.BundlingOptions{ GoBuildFlags: jsii.Strings(`-ldflags "-s -w"`), }, Environment: &map[string]*string{ "BUCKET_NAME": bucket.BucketName(), "TABLE_NAME": table.TableName(), }, })
  • 使用 alpha 模块awscdklambdagoalpha/v2的NewGoFunction:CDK 在合成阶段自动对./lambda-handler目录下的 Go 代码执行编译与打包(bundling),无需手动预编译二进制;
  • 运行时为PROVIDED_AL2023(Go 自定义运行时基于 Amazon Linux 2023);
  • -ldflags "-s -w"用于剥离符号表与调试信息,减小产物体积;
  • 通过环境变量把BUCKET_NAME与TABLE_NAME注入 Lambda,处理器运行时无需硬编码资源名。

4. 授权 Rekognition 识别权限

statement := awsiam.NewPolicyStatement(&awsiam.PolicyStatementProps{ Actions: &[]*string{jsii.String("rekognition:DetectLabels")}, Resources: &[]*string{jsii.String("*")}, }) function.AddToRolePolicy(statement)
  • 为 Lambda 执行角色追加一条内联策略,仅授予rekognition:DetectLabels一个动作;
  • 注意Resources: "*":Rekognition 的DetectLabels接口不需要指定具体的资源 ARN,属于 API 级权限,此处通配符是必要的。

5. S3 事件通知:按文件后缀过滤触发

//Add event notifications for bucket bucket.AddEventNotification(awss3.EventType_OBJECT_CREATED, awss3notifications.NewLambdaDestination(function), &awss3.NotificationKeyFilter{ Suffix: jsii.String(".jpg"), }) bucket.AddEventNotification(awss3.EventType_OBJECT_CREATED, awss3notifications.NewLambdaDestination(function), &awss3.NotificationKeyFilter{ Suffix: jsii.String(".jpeg"), }) bucket.AddEventNotification(awss3.EventType_OBJECT_CREATED, awss3notifications.NewLambdaDestination(function), &awss3.NotificationKeyFilter{ Suffix: jsii.String(".png"), })
  • 监听OBJECT_CREATED(对象创建)事件,将 Lambda 作为事件目标(destination),CDK 会自动生成对应的 Lambda 资源策略(resource policy)允许 S3 调用;
  • 通过NotificationKeyFilter.Suffix限定只处理.jpg、.jpeg、.png三种图片格式,避免其他类型对象(如文本、视频)触发无意义的识别调用。

6. 授予读写权限

table.GrantReadWriteData(function) bucket.GrantReadWrite(function, nil)
  • table.GrantReadWriteData自动为 Lambda 执行角色附加 DynamoDB 表的读写策略;
  • bucket.GrantReadWrite附加 S3 桶的读写策略,让 Lambda 能从桶中读取图片对象(供 Rekognition 拉取)。

7. CloudFormation 输出(Outputs)

栈末尾定义了 4 个输出(见 rekognition-lambda-s3-trigger.go#L82-L96):

输出逻辑名含义
UploadImageToS3直接给出可复制的上传命令:aws s3 cp <local-path-to-image> s3://<bucket>/,用于触发识别流程
DynamoDBTableDynamoDB 表名,识别结果存储位置
LambdaFunctionLambda 函数名
LambdaFunctionLogsLambda 对应的 CloudWatch 日志组名,方便排查运行日志

Lambda 处理器源码解析

处理器位于 lambda-handler/main.go,通过lambda.Start(handler)注册入口,使用 AWS SDK for Go v2(aws-sdk-go-v2)访问 Rekognition 与 DynamoDB。

事件结构解析

type Event struct { Records []struct { S3 struct { Object struct { Key string `json:"key"` } `json:"object"` } `json:"s3"` } `json:"Records"` }

只解析所需字段:Records[0].S3.Object.Key即被上传对象的 key(图片名),并以此为图片名与识别结果的关联键。

调用 Rekognition DetectLabels

detectLabelsInput := &rekognition.DetectLabelsInput{ Image: &rekognitionTypes.Image{ S3Object: &rekognitionTypes.S3Object{ Bucket: aws.String(os.Getenv("BUCKET_NAME")), Name: aws.String(key), }, }, MaxLabels: aws.Int32(10), MinConfidence: aws.Float32(70), } detectLabelsOutput, err := rekognitionClient.DetectLabels(context.TODO(), detectLabelsInput)
  • 图片源直接指向 S3 对象,Rekognition 服务端从桶中拉取图片,Lambda 无需下载图片再上传(当然,读取桶对象仍需 S3 读权限,即前面bucket.GrantReadWrite的作用);
  • MaxLabels = 10:最多返回 10 个标签;
  • MinConfidence = 70:只保留置信度 ≥ 70% 的标签,过滤掉低置信度的噪声结果;
  • 识别出的标签名收集进labels切片并通过fmt.Println输出到 CloudWatch 日志。

写入 DynamoDB(防重复)

putItemInput := &dynamodb.PutItemInput{ TableName: aws.String(tableName), Item: map[string]types.AttributeValue{ "image_name": &types.AttributeValueMemberS{Value: key}, "labels": &types.AttributeValueMemberS{Value: fmt.Sprintf("%v", labels)}, }, ConditionExpression: aws.String("attribute_not_exists(image_name)"), }
  • 主键image_name存入图片 key,属性labels存入标签列表(以 Go 切片格式化字符串存储);
  • 通过ConditionExpression: attribute_not_exists(image_name)保证同一图片只写入一次,重复上传同名对象不会产生多条记录——这是一个容易被忽略的幂等细节。

部署与验证

依赖与运行环境

先确认满足前置条件:已配置 AWS 凭证(aws configure或环境变量),且本机已安装 AWS CDK CLI 与 Go 1.25+(go.mod 声明go 1.25.0)。

管理 Go 依赖

按 README 的说明,运行:

go mod tidy

该命令根据go.mod/go.sum整理依赖。本项目依赖包括 CDK Go 模块awscdk/v2 v2.262.0、alpha 模块awscdklambdagoalpha/v2,以及aws-lambda-go、aws-sdk-go-v2(dynamodb、rekognition)等,详见 go.mod。

合成 CloudFormation 模板

cdk synth

cdk.json 中的app字段定义为:

{ "app": "go mod download && go run rekognition-lambda-s3-trigger.go" }

即每次合成时先自动下载 Go 依赖再运行栈程序,把生成的 CloudFormation 模板输出到cdk.out目录。cdk synth不产生任何真实云资源,适合在部署前审查生成的模板。

部署

cdk deploy

cdk deploy将栈部署(或重新部署)到你的 AWS 账户。部署完成后,根据控制台输出的UploadImageToS3提示,上传一张图片触发流程:

aws s3 cp <local-path-to-image> s3://<bucket>/

随后可到 DynamoDB 控制台查看以image_name为分区键的记录,或在 CloudWatch 日志组中查看 Lambda 打印的标签列表。

单元断言测试

仓库附带 rekognition-lambda-s3-trigger_test.go,属于 CDK 模板断言测试(无需真实部署即可运行):通过assertions.Template_FromStack合成栈模板,断言资源数量与属性,例如:

  • AWS::S3::Bucket资源数量为 1;
  • AWS::DynamoDB::Table的AttributeDefinitions包含image_name(类型S)。

可运行go test ./...或cdk test验证基础设施代码的正确性,防止后续改动破坏资源配置。

扩展与注意事项

  • 支持更多图片格式:若需处理.gif、.webp等格式,参照 rekognition-lambda-s3-trigger.go#L69-L77 追加AddEventNotification与对应的NotificationKeyFilter.Suffix即可;
  • 调整识别精度:MaxLabels与MinConfidence两个参数可根据业务在 lambda-handler/main.go#L41-L50 中调整,例如电商图片可提高置信度阈值;
  • 生产化改造:示例中使用RemovalPolicy_DESTROY、env()返回nil等开发友好的默认值,生产部署应显式指定环境(account/region),并为桶、表设置合适的删除保护与备份策略;
  • 结果存储结构:labels以字符串形式存储 Go 切片格式化结果,若需要结构化查询(如按标签检索),可考虑将标签拆分为 List 类型属性或单独建立二级索引。

本文所涉及的完整代码、测试与配置均可直接参考 go/rekognition-lambda-s3-trigger 目录下的源码进行二次开发。

【免费下载链接】aws-cdk-examples

Example projects using the AWS CDK

项目地址:https://gitcode.com/gh_mirrors/aw/aws-cdk-examples
点击查看免费下载
上一篇:CyclopsLevelBuilder性能优化技巧:让你的大型关卡运行如丝般顺滑
下一篇:Flutter GetX网络请求终极指南:掌握API错误处理与重试机制

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进