架构概述
本教程将构建一个完整的无服务器图片上传系统:
API Gateway: 提供 RESTful API 接口
Lambda: 处理业务逻辑(图片处理、验证)
S3: 存储图片文件
DynamoDB: 记录图片元数据
应用场景: 用户通过 API 上传图片,Lambda 自动处理并存储到 S3,同时在 DynamoDB 中记录上传信息。
架构流程图

客户端 → API Gateway → Lambda → S3 (存储图片)
↓
DynamoDB (记录元数据)第一步:创建 S3 存储桶

# 创建 S3 存储桶
aws s3 mb s3://my-image-upload-bucket-20251229 --region us-east-1
# 启用版本控制(可选)
aws s3api put-bucket-versioning \
--bucket my-image-upload-bucket-20251229 \
--versioning-configuration Status=Enabled第二步:创建 DynamoDB 表

aws dynamodb create-table \
--table-name ImageMetadata \
--attribute-definitions \
AttributeName=imageId,AttributeType=S \
--key-schema \
AttributeName=imageId,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region us-east-1表结构设计:
imageId: 主键,唯一标识fileName: 原始文件名s3Key: S3 对象键uploadTime: 上传时间戳fileSize: 文件大小contentType: 文件类型
第三步:创建 IAM 角色

Lambda 需要访问 S3 和 DynamoDB 的权限。
创建信任策略文件 trust-policy.json:
cat > trust-policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF创建权限策略文件 lambda-policy.json:
cat > lambda-policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": "arn:aws:s3:::my-image-upload-bucket-20251229/*"
},
{
"Effect": "Allow",
"Action": [
"dynamodb:PutItem",
"dynamodb:GetItem"
],
"Resource": "arn:aws:dynamodb:us-east-1:*:table/ImageMetadata"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
EOF执行创建命令:
# 创建角色
aws iam create-role \
--role-name ImageUploadLambdaRole \
--assume-role-policy-document file://trust-policy.json
# 附加自定义策略
aws iam put-role-policy \
--role-name ImageUploadLambdaRole \
--policy-name ImageUploadPolicy \
--policy-document file://lambda-policy.json第四步:编写 Lambda 函数

创建 Lambda 函数代码 lambda_function.py:
cat > lambda_function.py << EOF
import json
import boto3
import base64
import uuid
from datetime import datetime
s3 = boto3.client('s3')
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('ImageMetadata')
BUCKET_NAME = 'my-image-upload-bucket-20251229'
def lambda_handler(event, context):
try:
# 解析请求体
body = json.loads(event['body'])
image_data = body['image'] # Base64 编码的图片
file_name = body.get('fileName', 'unknown.jpg')
content_type = body.get('contentType', 'image/jpeg')
# 解码 Base64 图片
image_bytes = base64.b64decode(image_data)
file_size = len(image_bytes)
# 生成唯一 ID 和 S3 键
image_id = str(uuid.uuid4())
s3_key = f"images/{image_id}/{file_name}"
# 上传到 S3
s3.put_object(
Bucket=BUCKET_NAME,
Key=s3_key,
Body=image_bytes,
ContentType=content_type
)
# 记录到 DynamoDB
upload_time = datetime.utcnow().isoformat()
table.put_item(
Item={
'imageId': image_id,
'fileName': file_name,
's3Key': s3_key,
'uploadTime': upload_time,
'fileSize': file_size,
'contentType': content_type,
'bucketName': BUCKET_NAME
}
)
# 返回成功响应
return {
'statusCode': 200,
'headers': {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
'body': json.dumps({
'message': '图片上传成功',
'imageId': image_id,
's3Url': f"https://{BUCKET_NAME}.s3.amazonaws.com/{s3_key}",
'fileSize': file_size
})
}
except Exception as e:
return {
'statusCode': 500,
'headers': {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
'body': json.dumps({
'message': '上传失败',
'error': str(e)
})
}
EOF打包并部署:
# 打包代码
zip lambda_function.zip lambda_function.py
# 创建 Lambda 函数
aws lambda create-function \
--function-name ImageUploadFunction \
--runtime python3.11 \
--role arn:aws:iam::654654569766:role/ImageUploadLambdaRole \
--handler lambda_function.lambda_handler \
--zip-file fileb://lambda_function.zip \
--timeout 30 \
--memory-size 256 \
--region us-east-1第五步:创建 API Gateway


# 创建 REST API
aws apigateway create-rest-api \
--name ImageUploadAPI \
--description "图片上传 API" \
--region us-east-1
# 获取 API ID(从上一步输出中获取)
API_ID=6tduhktb98
# 获取根资源 ID
ROOT_ID=$(aws apigateway get-resources \
--rest-api-id $API_ID \
--query 'items[0].id' \
--output text)
# 创建 /upload 资源
RESOURCE_ID=$(aws apigateway create-resource \
--rest-api-id $API_ID \
--parent-id $ROOT_ID \
--path-part upload \
--query 'id' \
--output text)
# 创建 POST 方法
aws apigateway put-method \
--rest-api-id $API_ID \
--resource-id $RESOURCE_ID \
--http-method POST \
--authorization-type NONE
# 集成 Lambda
LAMBDA_ARN="arn:aws:lambda:us-east-1:<YOUR_ACCOUNT_ID>:function:ImageUploadFunction"
aws apigateway put-integration \
--rest-api-id $API_ID \
--resource-id $RESOURCE_ID \
--http-method POST \
--type AWS_PROXY \
--integration-http-method POST \
--uri "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/$LAMBDA_ARN/invocations"
# 授权 API Gateway 调用 Lambda
aws lambda add-permission \
--function-name ImageUploadFunction \
--statement-id apigateway-invoke \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-east-1:<YOUR_ACCOUNT_ID>:$API_ID/*/*"
# 部署 API
aws apigateway create-deployment \
--rest-api-id $API_ID \
--stage-name prodAPI 端点: https://<API_ID>.execute-api.us-east-1.amazonaws.com/prod/upload
第六步:测试 API


使用 curl 测试:
# 将图片转换为 Base64
IMAGE_BASE64=$(base64 -w 0 test-image.jpg)
#构造JSON(注意:手动确保没有非法字符)
cat > /tmp/upload-payload.json <<EOF
{
"image": "$IMAGE_BASE64",
"fileName": "test-image.jpg",
"contentType": "image/jpeg"
}
EOF
# 发送请求
curl -X POST \
-H "Content-Type: application/json" \
--data-binary @/tmp/upload-payload.json \
https://<API_ID>.execute-api.us-east-1.amazonaws.com/prod/upload

使用 Python 测试:
import requests
import base64
# 读取图片
with open('test-image.jpg', 'rb') as f:
image_data = base64.b64encode(f.read()).decode('utf-8')
# 发送请求
url = 'https://<API_ID>.execute-api.us-east-1.amazonaws.com/prod/upload'
payload = {
'image': image_data,
'fileName': 'test-image.jpg',
'contentType': 'image/jpeg'
}
response = requests.post(url, json=payload)
print(response.json())第七步:查询上传记录

查询 DynamoDB:
aws dynamodb get-item \
--table-name ImageMetadata \
--key '{"imageId": {"S": "<your-image-id>"}}' \
--region us-east-1
aws dynamodb get-item \
--table-name ImageMetadata \
--key '{"imageId": {"S": "f1732ea8-40d4-4d7c-af5f-8abe8d1b24bf"}}' \
--region us-east-1扫描所有记录:
aws dynamodb scan \
--table-name ImageMetadata \
--region us-east-1


优化建议
1. 添加图片格式验证
ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/gif']
if content_type not in ALLOWED_TYPES:
return {'statusCode': 400, 'body': json.dumps({'error': '不支持的图片格式'})}2. 添加文件大小限制
MAX_SIZE = 5 * 1024 * 1024 # 5MB
if file_size > MAX_SIZE:
return {'statusCode': 400, 'body': json.dumps({'error': '文件过大'})}3. 使用 S3 预签名 URL(更高效)
对于大文件,建议使用预签名 URL 直接上传到 S3:
def generate_presigned_url(file_name):
s3_key = f"images/{uuid.uuid4()}/{file_name}"
url = s3.generate_presigned_url(
'put_object',
Params={'Bucket': BUCKET_NAME, 'Key': s3_key},
ExpiresIn=3600
)
return url, s3_key4. 添加 CloudWatch 监控
aws cloudwatch put-metric-alarm \
--alarm-name ImageUploadErrors \
--alarm-description "Lambda 错误率过高" \
--metric-name Errors \
--namespace AWS/Lambda \
--statistic Sum \
--period 300 \
--threshold 10 \
--comparison-operator GreaterThanThreshold5. 启用 S3 生命周期策略
aws s3api put-bucket-lifecycle-configuration \
--bucket my-image-upload-bucket-20251229 \
--lifecycle-configuration file://lifecycle.jsonlifecycle.json:
{
"Rules": [
{
"Id": "ArchiveOldImages",
"Status": "Enabled",
"Transitions": [
{
"Days": 90,
"StorageClass": "GLACIER"
}
]
}
]
}成本估算
基于每月 10,000 次上传(每张图片 1MB):
安全最佳实践
启用 API 密钥或 Cognito 认证
配置 S3 存储桶策略,禁止公开访问
启用 CloudTrail 审计日志
使用 WAF 防护 API Gateway
加密 S3 对象(SSE-S3 或 SSE-KMS)
故障排查
Lambda 超时
增加超时时间:
--timeout 60检查网络配置(VPC)
S3 上传失败
验证 IAM 权限
检查存储桶名称和区域
DynamoDB 写入失败
确认表名正确
检查 IAM 角色权限
总结
本教程展示了如何构建一个完整的无服务器图片上传系统,涵盖:
✅ API Gateway 配置 RESTful 接口
✅ Lambda 处理业务逻辑
✅ S3 存储图片文件
✅ DynamoDB 记录元数据
✅ IAM 权限配置
✅ 测试和优化
这是一个典型的 AWS 无服务器架构模式,可扩展到文件管理、内容分发等多种场景。