Flask(Python)渲染HTML模板时新上传图片如何不替换原有图片
问题根源
- 两个上传处理函数同时绑定了
/路径的POST方法,Flask框架只会生效最后定义的upload_style函数,前面写的upload_content逻辑会被完全覆盖,根本不会触发 - 后端每次上传完成仅向前端传递最新上传的单张图片文件名,前端模板也只预留了单个图片的渲染位置,没有历史图片的渲染逻辑
- 用单个全局变量存储图片对象,新图片上传后会直接覆盖旧变量的值,没有设计存储历史上传记录的结构
- 两个上传表单的提交地址都写的
/,没有做区分标识,后端无法判断当前提交的是内容图还是风格图
修复步骤
- 拆分上传路由:内容图上传接口绑定
/upload/content,风格图上传接口绑定/upload/style - 新增全局列表分别存储已上传的内容图、风格图文件名,新上传的文件追加到对应列表而不是覆盖原有值
- 补充根路径
/的GET请求处理逻辑,页面初始加载时把两类图片的完整列表传给前端模板 - 修改前端模板,用循环遍历图片列表,渲染所有已上传的图片,不要只渲染单张
- 调整两个表单的提交地址,分别指向对应的上传接口
修复后后端代码
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
相关产品推荐
相关产品推荐

