将文件从s3 Bucket下载到USERS计算机

Ari*_*elt 5 python api operating-system amazon-s3 web-development-server

目标

从s3 Bucket下载文件到用户计算机.

上下文

我正在为React应用程序开发Python/Flask API.当用户单击前端的"下载"按钮时,我想将相应的文件下载到他们的计算机上.

我试过的

import boto3 s3 = boto3.resource('s3') s3.Bucket('mybucket').download_file('hello.txt', '/tmp/hello.txt')

我目前正在使用一些代码来查找downloads文件夹的路径,然后将该路径作为第二个参数插入download_file(),以及他们尝试下载的存储桶上的文件.

这在本地工作,测试运行正常,但一旦部署,我遇到了问题.代码将找到SERVER的下载路径,并在那里下载文件.

解决这个问题的最佳方法是什么?我已经研究过,无法找到一个很好的解决方案,可以将文件从s3存储桶下载到用户下载文件夹.非常感谢任何帮助/建议.

Raj*_*oni 10

解决此问题的更好方法是创建 presigned url。这为您提供了一个在一定时间内有效的临时 URL。它还删除了作为 AWS s3 存储桶之间代理的 Flask 服务器,从而减少了用户的下载时间。

def get_attachment_url():
   bucket = 'BUCKET_NAME'
   key = 'FILE_KEY'

   client: boto3.s3 = boto3.client(
     's3',
     aws_access_key_id=YOUR_AWS_ACCESS_KEY,
     aws_secret_access_key=YOUR_AWS_SECRET_KEY
   )

   return client.generate_presigned_url('get_object',
                                     Params={'Bucket': bucket, 'Key': key},
                                     ExpiresIn=60) `
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的答案。我已经投了赞成票。但是,这里需要注意的一件事是,如果您使用了 SSE-C(使用客户提供的密钥进行服务器端加密),那么它将不起作用。 (2认同)

All*_*ter 9

您不需要将文件保存到服务器.您只需将文件下载到内存中,然后构建Response包含该文件的对象即可.

from flask import Flask, Response
from boto3 import client

app = Flask(__name__)


def get_client():
    return client(
        's3',
        'us-east-1',
        aws_access_key_id='id',
        aws_secret_access_key='key'
    )


@app.route('/blah', methods=['GET'])
def index():
    s3 = get_client()
    file = s3.get_object(Bucket='blah-test1', Key='blah.txt')
    return Response(
        file['Body'].read(),
        mimetype='text/plain',
        headers={"Content-Disposition": "attachment;filename=test.txt"}
    )


app.run(debug=True, port=8800)
Run Code Online (Sandbox Code Playgroud)

这对于小文件是可以的,用户没有任何有意义的等待时间.但是对于较大的文件,这很好地影响了UX.该文件需要完全下载到服务器,然后下载给用户.因此,要解决此问题,请使用方法的Range关键字参数get_object:

from flask import Flask, Response
from boto3 import client

app = Flask(__name__)


def get_client():
    return client(
        's3',
        'us-east-1',
        aws_access_key_id='id',
        aws_secret_access_key='key'
    )


def get_total_bytes(s3):
    result = s3.list_objects(Bucket='blah-test1')
    for item in result['Contents']:
        if item['Key'] == 'blah.txt':
            return item['Size']


def get_object(s3, total_bytes):
    if total_bytes > 1000000:
        return get_object_range(s3, total_bytes)
    return s3.get_object(Bucket='blah-test1', Key='blah.txt')['Body'].read()


def get_object_range(s3, total_bytes):
    offset = 0
    while total_bytes > 0:
        end = offset + 999999 if total_bytes > 1000000 else ""
        total_bytes -= 1000000
        byte_range = 'bytes={offset}-{end}'.format(offset=offset, end=end)
        offset = end + 1 if not isinstance(end, basestring) else None
        yield s3.get_object(Bucket='blah-test1', Key='blah.txt', Range=byte_range)['Body'].read()


@app.route('/blah', methods=['GET'])
def index():
    s3 = get_client()
    total_bytes = get_total_bytes(s3)

    return Response(
        get_object(s3, total_bytes),
        mimetype='text/plain',
        headers={"Content-Disposition": "attachment;filename=test.txt"}
    )


app.run(debug=True, port=8800)
Run Code Online (Sandbox Code Playgroud)

这将以1MB的块下载文件,并在下载时将其发送给用户.这两个都已经使用40MB .txt文件进行了测试.