如何让用户直接把图片上传到对象存储 S3
Posted wzlinux
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何让用户直接把图片上传到对象存储 S3相关的知识,希望对你有一定的参考价值。
问题背景
在 Web 和移动应用程序中,为用户提供上传数据的功能是很常见的。您的应用程序可能允许用户上传 PDF 和文件、照片或视频等媒体。每个现代 Web 服务器技术都有允许此功能的机制。通常,在基于服务器的环境中,该过程遵循此流程:
- The user uploads the file to the application server.
- The application server saves the upload to a temporary space for processing.
- The application transfers the file to a database, file server, or object store for persistent storage.
虽然该过程很简单,但它可能会对繁忙应用程序中的 Web 服务器的性能产生显着的副作用。媒体上传通常很大,因此传输这些媒体可能会占用大量的网络 I/O 和服务器 CPU 时间。您还必须管理传输状态以确保成功上传整个对象,并管理重试和错误。
这对于具有尖峰流量模式的应用程序具有挑战性。例如,在专门发送节日问候的 Web 应用程序中,它可能仅在节假日前后遇到大部分流量。如果成千上万的用户尝试在同一时间上传媒体,这需要您横向扩展应用服务器并确保有足够的网络带宽可用。
通过将这些文件直接上传到 Amazon S3,您可以避免通过您的应用程序服务器代理这些请求。这可以显着减少网络流量和服务器 CPU 使用率,并使您的应用程序服务器能够在繁忙时段处理其他请求。 S3 还具有高可用性和持久性,使其成为用户上传的理想持久存储。
无服务上传到 S3
架构
当您直接上传到 S3 存储桶时,您必须首先从 Amazon S3 服务请求签名 URL。然后,您可以使用签名 URL 直接上传。这是您的应用程序前端的两步过程:
- Call an Amazon API Gateway endpoint, which invokes the s3-presigned-url Lambda function. This gets a signed URL from the S3 bucket.
- Directly upload the file from the application to the S3 bucket.
部署
我们使用 AWS SAM 来部署。
git clone https://github.com/wangzan18/s3-presigned-urls.git
cd s3-presigned-urls
sam deploy --guided
当部署完成之后,会输出一个 API 的终端节点:
测试
测试为两个步骤,第一步是通过请求获取一个预签名的 URL,第二步是向这个 URL PUT 一个图片。
第一步:
{"url": "https://s3-presigned-url-s3uploadbucket-41pnizqboni7.s3.amazonaws.com/3504834451.jpg?AWSAccessKeyId=ASIA5NAGHF6N7XQ72YIF&Signature=F5N0pymJB1J7ZpzgRRy6FLSX2BY%3D&content-type=image%2Fjpeg&x-amz-security-token=IQoJb3JpZ2luX2VjEHsaCWV1LXdlc3QtMSJIMEYCIQDiz5UvT4x5bMGbR4tyH4Af9jTiPeGaRRI%2F%2B24MrxasmwIhAPOFqy3A7hq5inu6Jbppc37xhnU3ygZn1FFTJeUhNpzYKsICCIT%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQAhoMOTIxMjgzNTM4ODQzIgzb0OFdLrNq1xQ5rVMqlgJ5xuc80KvUyYF6FtRahmpkPVZkh8G4BG2SqFIDCXYRQJqOVpIKXplt3ct5DjZ%2FRAjosV85lnB4SxDBC6PvFaRFsRfmk6wmmJJBqNAr6hXOr%2FMnlpwe2dwC1ks7OP7xHmZqGnDjlNebLhs1x9osb61BYfmrwjJg18LqFAra1O%2FvoPJcxbWniHwYxuGAYacs4%2FcXMKKQadyOzrdv6w4g54PprSZhFL5QHVCWx2FpNIqnb7YOUDKF%2BydQMnFywuA6lnShOeZLAWjTExdYpZCIA0i0vcEBPDN2r86LYv%2BJqpTbb%2BDlnGlWAz6HxLwbWrvQ8vNOVt0coTeOqjmbdhdwFoonhv9oArH%2FnXk5yKPLYuKGolFAmuFdPzC6t4iIBjqZAapNMWAxIdJJdBojvErX2Ybnwr%2F2b%2B0aq%2BIQ0uQb3IfZeopurj00xI1L3VX0qFbP%2FMn2yhhTy6rjAVdBgBhXgBiw86dUdnke9e4wvw%2Bwuq3iREFIoKKRyYrZ4yjUoOKqaOHdFESfR6mASv7DER0HzYhemUf39NroIDGKPYqvJMfnKoXB4wWSuKdt2DXD8%2FVLhNoB%2FjIr4QOOYw%3D%3D&Expires=1627531724", "key": "3504834451.jpg"}
拿到这个 URL,我们进行第二步,可以直接点击 URL:
这里请求需要改为 PUT,在 Body 里面选择 binary 来上传图片,然后点击提交。
可以看到请求成功,我们登录到 S3 看下图片上传如何:
这里需要注意的是,因为我们的预签名 URL 的 Key 名称已经确定了,不管我们上传的图片是什么名称,最终结果都会显示 Key 的名称。
前端测试
在我的代码仓库里面,还有一个前端的页面,可以传到 S3,然后开放公网访问,提交图片。
仅供简单测试,最重要的是后面移动应用如何对接。
模板介绍
template.yaml
这个文件是 SAM 最主要的配置文件,通过这一段参数,创建一个 HTTP API Gateway,允许一些请求参数。
# HTTP API
MyApi:
Type: AWS::Serverless::HttpApi
Properties:
# CORS configuration - this is open for development only and should be restricted in prod.
# See https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-httpapi-httpapicorsconfiguration.html
CorsConfiguration:
AllowMethods:
- GET
- POST
- DELETE
- OPTIONS
AllowHeaders:
- "*"
AllowOrigins:
- "*"
下面这个资源比较重要一步,需要确保 Lambda 对 S3 有写的权限,并且事件驱动为 HTTP API Gateway。
S3PreSignedUrlFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: s3-presigned-url/
Handler: app.lambda_handler
Policies:
- S3WritePolicy:
BucketName: !Ref S3UploadBucket
## This permission allows the Lambda function to request signed URLs
## for objects that will be publicly readable. Uncomment if you want this ACL.
# - Statement:
# - Effect: Allow
# Resource: !Sub \'arn:aws:s3:::${S3UploadBucket}/\'
# Action:
# - s3:putObjectAcl
Environment:
Variables:
UploadBucket: !Ref S3UploadBucket
Runtime: python3.7
Events:
S3PreSignedApi:
Type: HttpApi # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /upload
Method: get
ApiId: !Ref MyApi
app.py
整个申请预签名 URL 的函数比较简单,繁琐的功能 SDK 都已经封装好了,函数会自动生成一个随机的 Key,然后返回给请求者一个 URL,通过这个 URL 可以使用 PUT 请求上传参数
import boto3
import json
import os
import random
def lambda_handler(event, context):
bucket = os.environ.get(\'UploadBucket\')
key = random.getrandbits(32)
url = boto3.client(\'s3\').generate_presigned_url(
ClientMethod=\'put_object\',
Params={\'Bucket\': bucket,
\'Key\': str(key) + \'.jpg\',
\'ContentType\': \'image/jpeg\'},
ExpiresIn=3600)
return {
"statusCode": 200,
"body": json.dumps({
"url": url,
"key": str(key) + \'.jpg\'
}),
}
参考文档:
https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/PresignedUrlUploadObject.html
欢迎大家扫码关注,获取更多信息
以上是关于如何让用户直接把图片上传到对象存储 S3的主要内容,如果未能解决你的问题,请参考以下文章