如何让用户直接把图片上传到对象存储 S3

h1>问题背景

在 Web 和移动应用程序中,为用户提供上传数据的功能是很常见的。您的应用程序可能允许用户上传 PDF 和文件、照片或视频等媒体。每个现代 Web 服务器技术都有允许此功能的机制。通常,在基于服务器的环境中,该过程遵循此流程:

image-20210729105437964
  1. The user uploads the file to the application server.
  2. The application server saves the upload to a temporary space for processing.
  3. 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 直接上传。这是您的应用程序前端的两步过程:

image-20210729110145060
  1. Call an Amazon API Gateway endpoint, which invokes the s3-presigned-url Lambda function. This gets a signed URL from the S3 bucket.
  2. 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 的终端节点:

image-20210729110709738

测试

测试为两个步骤,第一步是通过请求获取一个预签名的 URL,第二步是向这个 URL PUT 一个图片。

第一步:

image-20210729110901114
{"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 来上传图片,然后点击提交。

image-20210729111124169

可以看到请求成功,我们登录到 S3 看下图片上传如何:

image-20210729111225718

这里需要注意的是,因为我们的预签名 URL 的 Key 名称已经确定了,不管我们上传的图片是什么名称,最终结果都会显示 Key 的名称。

前端测试

在我的代码仓库里面,还有一个前端的页面,可以传到 S3,然后开放公网访问,提交图片。

image-20210729111921620

image-20210729111940651

仅供简单测试,最重要的是后面移动应用如何对接。

模板介绍

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'
    }),
}</code></pre>

需要注意的是,因为我们要上传的是图片,所以需要在 Params 添加一个参数'ContentType': 'image/jpeg'

参考文档:

https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/PresignedUrlUploadObject.html

欢迎大家扫码关注,获取更多信息

如何让用户直接把图片上传到对象存储 S3
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好,实现文件流上传到亚马逊S3对象存储可以使用Java SDK提供的TransferManager类来实现。下面是实现的步骤: 1. 引入依赖: ```xml <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-java-sdk-s3</artifactId> <version>1.11.973</version> </dependency> ``` 2. 创建S3Client对象: ```java AmazonS3 s3Client = AmazonS3ClientBuilder.standard() .withRegion(Regions.US_EAST_1) .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey))) .build(); ``` 3. 创建TransferManager对象: ```java TransferManager transferManager = TransferManagerBuilder.standard() .withS3Client(s3Client) .build(); ``` 4. 创建Upload对象并开始上传: ```java File file = new File("your-file-path"); Upload upload = transferManager.upload(bucketName, objectKey, file); try { upload.waitForCompletion(); } catch (InterruptedException e) { e.printStackTrace(); } ``` 其中,bucketName为存储桶名称,objectKey为上传到S3的对象名称,file为要上传的文件。 如果想要实现文件流上传,可以将文件流转换为InputStream对象,然后使用TransferManager的upload方法上传: ```java InputStream inputStream = new FileInputStream(file); ObjectMetadata metadata = new ObjectMetadata(); metadata.setContentLength(file.length()); Upload upload = transferManager.upload(bucketName, objectKey, inputStream, metadata); try { upload.waitForCompletion(); } catch (InterruptedException e) { e.printStackTrace(); } ``` 这样就可以实现文件流上传到亚马逊S3对象存储了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值