从私有 AWS S3 存储桶在 Flask 中提供静态文件
Serve static files in Flask from private AWS S3 bucket
我正在 Heroku 上开发一个 Flask 应用 运行,允许用户上传图片。该应用程序有一个页面以 table.
格式显示用户的图像
出于开发目的,我将上传的文件保存到 Heroku 的 ephemeral file system, and everything works fine: the images are correctly loaded and displayed (I am using the last method shown here,这意味着使用 send_from_directory())。现在我已将存储移动到 S3,并且我正在尝试调整代码。我使用 boto3 将文件上传到存储桶:它工作正常。 我的疑虑与用他们的图片填充用户页面的下载有关。
正如 here, I could set the file as "public-read" and use the URL (I think this is what Flask-S3 所解释的那样),但我宁愿不保留对文件的免费访问权限。 因此,我的解决方案尝试是将文件下载到 Heroku 的文件系统并再次使用 send_from_directory() 提供图像,如下所示:
app.py
@app.route('/download/<resource>')
def download_image(resource):
""" resource: name of the file to download"""
s3 = boto3.client('s3',
aws_access_key_id=current_app.config['S3_ACCESS_KEY'],
aws_secret_access_key=current_app.config['S3_SECRET_KEY'])
s3.download_file(current_app.config['S3_BUCKET_NAME'],
resource,
os.path.join('tmp',
resource))
return send_from_directory('tmp', # Heroku's filesystem
resource,
as_attachment=False)
然后,在模板中,我为图像生成 URL,如下所示:
...
<img src="{{ url_for('app.download_image',
resource=resource) }}" height="120" width="120">
...
它有效,但由于某些原因我认为这不是正确的方法:其中,我应该管理 Heroku 的文件系统以避免在 dynos 重启之间用完所有 space(我应该删除来自文件系统的图像)。
哪个是best/preferred方式,还要考虑性能?
非常感谢
首选方法是简单地 create a pre-signed URL 图片,然后 return 重定向到 URL。这使文件在 S3 中保持私有,但会生成一个临时的、有时间限制的 URL,可用于直接从 S3 下载文件。这将大大减少服务器上发生的工作量,以及服务器消耗的数据传输量。像这样:
@app.route('/download/<resource>')
def download_image(resource):
""" resource: name of the file to download"""
s3 = boto3.client('s3',
aws_access_key_id=current_app.config['S3_ACCESS_KEY'],
aws_secret_access_key=current_app.config['S3_SECRET_KEY'])
url = s3.generate_presigned_url('get_object', Params = {'Bucket': 'S3_BUCKET_NAME', 'Key': resource}, ExpiresIn = 100)
return redirect(url, code=302)
如果您不喜欢该解决方案,您至少应该考虑从 S3 流式传输文件内容,而不是将其写入文件系统。
我正在 Heroku 上开发一个 Flask 应用 运行,允许用户上传图片。该应用程序有一个页面以 table.
格式显示用户的图像出于开发目的,我将上传的文件保存到 Heroku 的 ephemeral file system, and everything works fine: the images are correctly loaded and displayed (I am using the last method shown here,这意味着使用 send_from_directory())。现在我已将存储移动到 S3,并且我正在尝试调整代码。我使用 boto3 将文件上传到存储桶:它工作正常。 我的疑虑与用他们的图片填充用户页面的下载有关。
正如 here, I could set the file as "public-read" and use the URL (I think this is what Flask-S3 所解释的那样),但我宁愿不保留对文件的免费访问权限。 因此,我的解决方案尝试是将文件下载到 Heroku 的文件系统并再次使用 send_from_directory() 提供图像,如下所示:
app.py
@app.route('/download/<resource>')
def download_image(resource):
""" resource: name of the file to download"""
s3 = boto3.client('s3',
aws_access_key_id=current_app.config['S3_ACCESS_KEY'],
aws_secret_access_key=current_app.config['S3_SECRET_KEY'])
s3.download_file(current_app.config['S3_BUCKET_NAME'],
resource,
os.path.join('tmp',
resource))
return send_from_directory('tmp', # Heroku's filesystem
resource,
as_attachment=False)
然后,在模板中,我为图像生成 URL,如下所示:
...
<img src="{{ url_for('app.download_image',
resource=resource) }}" height="120" width="120">
...
它有效,但由于某些原因我认为这不是正确的方法:其中,我应该管理 Heroku 的文件系统以避免在 dynos 重启之间用完所有 space(我应该删除来自文件系统的图像)。
哪个是best/preferred方式,还要考虑性能? 非常感谢
首选方法是简单地 create a pre-signed URL 图片,然后 return 重定向到 URL。这使文件在 S3 中保持私有,但会生成一个临时的、有时间限制的 URL,可用于直接从 S3 下载文件。这将大大减少服务器上发生的工作量,以及服务器消耗的数据传输量。像这样:
@app.route('/download/<resource>')
def download_image(resource):
""" resource: name of the file to download"""
s3 = boto3.client('s3',
aws_access_key_id=current_app.config['S3_ACCESS_KEY'],
aws_secret_access_key=current_app.config['S3_SECRET_KEY'])
url = s3.generate_presigned_url('get_object', Params = {'Bucket': 'S3_BUCKET_NAME', 'Key': resource}, ExpiresIn = 100)
return redirect(url, code=302)
如果您不喜欢该解决方案,您至少应该考虑从 S3 流式传输文件内容,而不是将其写入文件系统。