我正在尝试使用 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 调用是否是“最佳实践”,为了尝试您的用例,我最终得到了:

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,因此它已正确实现并且仍处于“位置 0”。

关于python - 通过 Flask 和 BytesIO 显示来自 S3 的文件(图像),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59038662/

10-12 20:19