You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Flask应用中显示Amazon S3存储桶中的图片?部署至Heroku相关问题求助

解决Flask应用显示Amazon S3存储桶图片的问题

我来帮你一步步梳理问题所在,以及对应的解决方案——毕竟把S3图片整合到Flask再部署到Heroku,需要注意几个关键环节:

一、先搞定S3存储桶的基础访问权限

首先得确保你的S3桶和里面的图片能被外部访问到,分两种场景:

  • 公开访问场景(适合博客这类公开内容):
    给存储桶添加桶策略,允许外部读取对象。替换YOUR_BUCKET_NAME为你的实际桶名,策略内容如下:
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": "*",
          "Action": "s3:GetObject",
          "Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/*"
        }
      ]
    }
    
    同时要在桶的Block Public Access设置里,取消阻止公开访问的相关选项(公开桶就关闭这些限制)。
  • 私有桶场景(不想公开图片):
    这种情况需要用预签名URL临时授权访问,我后面会讲到具体实现。

二、在Flask代码里获取S3图片的有效URL

你的现有代码初始化了S3客户端,但缺少获取图片URL的逻辑。这里分两种情况处理:

情况1:公开桶——直接构造固定URL

公开桶里的图片可以通过固定格式的URL访问:https://YOUR_BUCKET_NAME.s3.amazonaws.com/IMAGE_FILENAME

修改你的home视图函数,把这个URL传递给模板(注意先补全缺失的osmath导入):

# 先补全缺失的导入
import os
import math
from flask import Flask, render_template, request, session, redirect, flash
from flask_sqlalchemy import SQLAlchemy
from werkzeug.utils import secure_filename
import boto3

local_server = True
app = Flask(__name__)
app.secret_key = 'super-secret-key'
s3 = boto3.client('s3',
                  aws_access_key_id=os.environ.get('AWS_ACCESS_KEY_ID'),
                  aws_secret_access_key= os.environ.get('AWS_SECRET_ACCESS_KEY'),
                  )
BUCKET_NAME='my s3 bucket name'

@app.route("/")
def home():
    posts = Posts.query.filter_by().all()
    last = math.ceil(len(posts)/int(params['no_of_posts']))
    page = request.args.get('page')
    if(not str(page).isnumeric()):
        page = 1
    page= int(page)
    posts = posts[(page-1)*int(params['no_of_posts']): (page-1)*int(params['no_of_posts'])+ int(params['no_of_posts'])]
    
    # 新增:构造S3里首页背景图的URL(假设图片名为home-bg.jpg)
    s3_bg_url = f"https://{BUCKET_NAME}.s3.amazonaws.com/home-bg.jpg"
    
    # 分页逻辑保持不变
    if (page==1):
        prev = "#"
        next = "/?page="+ str(page+1)
    elif(page==last):
        prev = "/?page=" + str(page - 1)
        next = "#"
    else:
        prev = "/?page=" + str(page - 1)
        next = "/?page=" + str(page + 1)
    
    # 把S3图片URL传递给模板
    return render_template('index.html', params=params, posts=posts, prev=prev, next=next, s3_bg_url=s3_bg_url)

情况2:私有桶——生成预签名URL

如果你的桶是私有的,不能直接访问,就需要生成有有效期的预签名URL(下面示例设置1小时有效期):

# 在home视图里替换构造URL的代码
s3_bg_url = s3.generate_presigned_url('get_object',
                                      Params={'Bucket': BUCKET_NAME,
                                              'Key': 'home-bg.jpg'},
                                      ExpiresIn=3600)

同样把这个URL传递给模板即可。

三、修改模板引用S3图片

现在修改index.html里的头部背景图引用,把原来的本地static路径换成传递过来的S3 URL:

<header class="masthead" style="background-image: url('{{ s3_bg_url }}')">

如果是帖子里的图片,假设你的Posts模型存储了S3图片的文件名(比如s3_image_key字段),可以在循环里这样引用:

{% for post in posts %}
<div class="post-preview">
    <a href="/post/{{post.slug}}">
        <h2 class="post-title">{{ post.title }} </h2>
        <!-- 公开桶直接构造URL -->
        <img src="https://{{ BUCKET_NAME }}.s3.amazonaws.com/{{ post.s3_image_key }}" alt="{{ post.title }}">
    </a>
    <p class="post-meta">Posted by <a href="#">Admin</a> on {{post.date}}</p>
</div>
{{post.content[0:143]}}
<hr>
{% endfor %}

如果是私有桶,建议在视图函数里给每个post生成预签名URL,再传递给模板使用。

四、部署到Heroku的关键配置

  1. 环境变量:在Heroku应用的「设置」里,添加AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY两个环境变量,确保Flask能正确读取。
  2. 依赖包:确保requirements.txt包含所有需要的依赖:
Flask==2.0.1
flask-sqlalchemy==2.5.1
boto3==1.21.0
werkzeug==2.0.1
gunicorn==20.1.0
  1. Procfile:创建Procfile文件,内容为:
web: gunicorn main:app

常见问题排查

  • 如果图片不显示,先直接在浏览器访问构造的S3 URL:
    • 打不开:检查桶策略、权限设置,或者文件名是否正确(S3区分大小写)
    • 能打开:检查Flask模板里的变量拼写是否正确,确认URL是否成功传递到模板
  • 本地测试时,可以用这段代码验证S3连接是否正常:
# 临时测试代码,放在main.py里运行
response = s3.list_objects_v2(Bucket=BUCKET_NAME)
print(response)

如果报错,说明AWS凭证或桶名配置有误。

内容的提问来源于stack exchange,提问作者sanket bhandari

火山引擎 最新活动