通过 Flask 和 BytesIO 显示来自 S3 的文件(图像)

Displaying a file (image) from S3 via Flask & BytesIO

我正在尝试使用 Boto3 从 AWS S3 中将文件直接提取到 BytesIO 对象中。这最终将用于操作下载的数据,但现在我只是想通过 Flask 将该文件直接提供给用户。据我了解,下面的 应该 工作,但没有。浏览器什么都不显示(只显示下载的几个字节的数据)。

(在这个例子中,我的示例文件是一个png)

from flask import Flask, send_from_directory, abort, Response, send_file, make_response
import boto3, botocore
import os
import io

AWS_ACCESS_KEY = os.environ['AWS_ACCESS_KEY'].rstrip()
AWS_SECRET_KEY = os.environ['AWS_SECRET_KEY'].rstrip()
S3_BUCKET = "static1"
app = Flask(__name__, static_url_path='/tmp')

@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
    s3 = boto3.client('s3', aws_access_key_id=AWS_ACCESS_KEY, aws_secret_access_key=AWS_SECRET_KEY,)
    file = io.BytesIO()
    metadata = s3.head_object(Bucket=S3_BUCKET, Key=path)
    conf = boto3.s3.transfer.TransferConfig(use_threads=False)
    s3.download_fileobj(S3_BUCKET, path, file)
    return send_file(file, mimetype=metadata['ContentType'])

if __name__ == '__main__':
     app.run(debug=True,port=3000,host='0.0.0.0')

如果我修改该核心例程以将 BytesIO 对象写入磁盘,然后将其读回新的 BytesIO 对象 - 它工作正常。如下:

def catch_all(path):
    s3 = boto3.client('s3', aws_access_key_id=AWS_ACCESS_KEY, aws_secret_access_key=AWS_SECRET_KEY,)
    file = io.BytesIO()
    metadata = s3.head_object(Bucket=S3_BUCKET, Key=path)
    conf = boto3.s3.transfer.TransferConfig(use_threads=False)
    s3.download_fileobj(S3_BUCKET, path, file)
    print(file.getvalue())
    fh = open("/tmp/test1.png","wb")
    fh.write(file.getvalue())
    fh.close()
    fh = open("/tmp/test1.png","rb")
    f2 = io.BytesIO(fh.read())
    fh.close
    print(f2.getvalue())
    return send_file(f2, mimetype=metadata['ContentType'])

绕着这个转了几天,很明显我错过了什么,我不确定是什么。该脚本 运行 在 Python 3.8 docker 容器中,其中包含 boto3/flask/etc.

的最新副本

倒回您的 BytesIO 对象应该可以解决问题,file.seek(0) 就在 send_file(...) 之前。

郑重声明,我不确定你的 boto3/botocore 调用是 "best practices",为了尝试你的用例,我最终得到了:

from boto3.session import Session

session = Session(
    aws_access_key_id=KEY_ID, aws_secret_access_key=ACCESS_KEY, region_name=REGION_NAME
)
s3 = session.resource("s3")


@base_bp.route("/test-stuff")
def test_stuff():
    a_file = io.BytesIO()
    s3_object = s3.Object(BUCKET, PATH)
    s3_object.download_fileobj(a_file)
    a_file.seek(0)
    return send_file(a_file, mimetype=s3_object.content_type)

它在从磁盘读取文件时起作用,因为您用文件的全部内容实例化了 BytesIO,因此它已正确完成并仍在 "position 0"。