You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask(Python)渲染HTML模板时新上传图片如何不替换原有图片

问题根源
  • 两个上传处理函数同时绑定了/路径的POST方法,Flask框架只会生效最后定义的upload_style函数,前面写的upload_content逻辑会被完全覆盖,根本不会触发
  • 后端每次上传完成仅向前端传递最新上传的单张图片文件名,前端模板也只预留了单个图片的渲染位置,没有历史图片的渲染逻辑
  • 用单个全局变量存储图片对象,新图片上传后会直接覆盖旧变量的值,没有设计存储历史上传记录的结构
  • 两个上传表单的提交地址都写的/,没有做区分标识,后端无法判断当前提交的是内容图还是风格图
修复步骤
  1. 拆分上传路由:内容图上传接口绑定/upload/content,风格图上传接口绑定/upload/style
  2. 新增全局列表分别存储已上传的内容图、风格图文件名,新上传的文件追加到对应列表而不是覆盖原有值
  3. 补充根路径/的GET请求处理逻辑,页面初始加载时把两类图片的完整列表传给前端模板
  4. 修改前端模板,用循环遍历图片列表,渲染所有已上传的图片,不要只渲染单张
  5. 调整两个表单的提交地址,分别指向对应的上传接口
修复后后端代码
from flask import Flask, render_template, request, redirect, url_for, flash
from werkzeug.utils import secure_filename
import cv2
import os

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'static/uploads'  # 提前确认这个路径存在,没有就手动创建
app.secret_key = 'your_custom_secret_key'  # flash功能必须配置密钥
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}

# 用列表存储已上传的文件名,不要用单变量覆盖
uploaded_content = []
uploaded_style = []
content = None
style = None

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

# 根路径GET请求,返回主页面
@app.route('/', methods=['GET'])
def index():
    return render_template('index.html', content_files=uploaded_content, style_files=uploaded_style)

# 内容图上传接口
@app.route('/upload/content', methods=['POST'])
def upload_content():
    if 'file' not in request.files:
        flash('No file part')
        return redirect(url_for('index'))
    file = request.files['file']
    if file.filename == '':
        flash('No image selected for uploading')
        return redirect(url_for('index'))
    if file and allowed_file(file.filename):
        filename = secure_filename(file.filename)
        save_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        file.save(save_path)
        print('upload content image filename: ' + filename, save_path)
        flash('Content image successfully uploaded')
        global content
        content = cv2.imread(save_path, cv2.IMREAD_GRAYSCALE)
        # 新文件名追加到列表,不覆盖
        if filename not in uploaded_content:
            uploaded_content.append(filename)
        return redirect(url_for('index'))
    else:
        flash('Allowed image types are - png, jpg, jpeg, gif')
        return redirect(url_for('index'))

# 风格图上传接口
@app.route('/upload/style', methods=['POST'])
def upload_style():
    if 'file' not in request.files:
        flash('No file part')
        return redirect(url_for('index'))
    file = request.files['file']
    if file.filename == '':
        flash('No image selected for uploading')
        return redirect(url_for('index'))
    if file and allowed_file(file.filename):
        filenamejr = secure_filename(file.filename)
        save_path = os.path.join(app.config['UPLOAD_FOLDER'], filenamejr)
        file.save(save_path)
        print('upload style image filename: ' + filenamejr, save_path)
        flash('Style image successfully uploaded')
        global style
        style = cv2.imread(save_path, cv2.IMREAD_GRAYSCALE)
        # 新文件名追加到列表,不覆盖
        if filenamejr not in uploaded_style:
            uploaded_style.append(filenamejr)
        return redirect(url_for('index'))
    else:
        flash('Allowed image types are - png, jpg, jpeg, gif')
        return redirect(url_for('index'))


@app.route('/display/<filename>')
def display_image(filename):
    return redirect(url_for('static', filename='uploads/' + filename), code=301)

if __name__ == '__main__':
    app.run(debug=True)
修复后前端模板代码(templates/index.html)
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>图像编辑应用</title>
    <style>
        .img-item {display: inline-block; margin: 10px;}
        .img-item img {max-width: 200px; max-height: 200px;}
    </style>
</head>
<body>
<div class="container">
    <div class="row">
        <h2>上传内容图</h2>
        <p>
            {% with messages = get_flashed_messages() %}
              {% if messages %}
                <ul>
                {% for message in messages %}
                  <li>{{ message }}</li>
                {% endfor %}
                </ul>
              {% endif %}
            {% endwith %}
        </p>
        <!-- 循环渲染所有已上传的内容图 -->
        <div class="uploaded-list">
            {% for file in content_files %}
            <div class="img-item">
                <img src="{{ url_for('display_image', filename=file) }}">
                <p>{{ file }}</p>
            </div>
            {% endfor %}
        </div>
        <!-- 表单提交到对应的内容图上传接口 -->
        <form method="post" action="/upload/content" enctype="multipart/form-data">
            <dl>
                <p>
                    <input type="file" name="file" class="form-control" autocomplete="off" required>
                </p>
            </dl>
            <p>
                <input type="submit" value="上传内容图" class="btn btn-info">
            </p>
        </form>
    </div>

    <div class="row">
        <h2>上传风格图</h2>
        <!-- 循环渲染所有已上传的风格图 -->
        <div class="uploaded-list">
            {% for file in style_files %}
            <div class="img-item">
                <img src="{{ url_for('display_image', filename=file) }}">
                <p>{{ file }}</p>
            </div>
            {% endfor %}
        </div>
        <!-- 表单提交到对应的风格图上传接口 -->
        <form method="post" action="/upload/style" enctype="multipart/form-data">
            <dl>
                <p>
                    <input type="file" name="file" class="form-control" autocomplete="off" required>
                </p>
            </dl>
            <p>
                <input type="submit" value="上传风格图" class="btn btn-info">
            </p>
        </form>
    </div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type=text/javascript>
    $(function() {
      $('a#test').on('click', function(e) {
        e.preventDefault()
        $.getJSON('/maskInter/',
            function() {
          //do nothing
        });
        return false;
      });
    });
</script>
</body>
</html>
注意事项
  • 提前在项目static目录下创建uploads文件夹,确保程序有写入权限
  • 上述代码用内存列表存储上传记录,服务重启后记录会清空,如果需要持久化存储可以换成数据库或者本地json文件存储
  • 如果需要区分不同用户的上传记录,不要用全局变量,要结合session做用户隔离

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 10:21:26