在 Flask 中实现多张图片上传并同时显示
文件上传是 Web 开发中的常见需求,尤其是多张图片上传并即时预览的场景。本文将以 Flask 框架为例,详细讲解如何实现多图片上传,并将上传后的图片在页面中显示出来。我们将涵盖前端表单设计、后端文件处理逻辑以及图片的展示方式,同时注重安全性与用户体验。
整体思路
要实现多张图片上传并显示,需要完成以下步骤:
前端使用 HTML 表单,设置
enctype="multipart/form-data",并使用<input type="file" multiple>允许选择多张图片。后端 Flask 视图函数接收上传文件列表,依次保存到指定目录(如
static/uploads/)。保存后,将图片的 URL 列表传递到模板,模板通过
<img>标签遍历显示。可以加入简单的文件类型校验和大小限制,防止恶意上传。
项目结构
假设项目目录如下:
project/ ├── app.py # Flask 主应用 ├── static/ │ └── uploads/ # 存放上传图片的文件夹 └── templates/ └── upload.html # 上传页面模板
请确保在 static/uploads/ 目录(若不存在则手动创建)中具有写入权限。
后端实现
在 app.py 中,我们创建一个 Flask 应用,定义上传路由,处理多个文件的接收和保存。
import os
from flask import Flask, render_template, request, redirect, url_for, flash
from werkzeug.utils import secure_filename
from pathlib import Path
app = Flask(__name__)
app.secret_key = 'your-secret-key-here' # 用于 flash 消息
# 配置上传目录和允许的文件扩展名
UPLOAD_FOLDER = Path('static/uploads')
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp'}
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
# 可选:限制最大文件大小为 16MB
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
def allowed_file(filename):
"""检查文件扩展名是否合法"""
return '.' in filename and
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/', methods=['GET', 'POST'])
def upload_images():
if request.method == 'POST':
files = request.files.getlist('images') # 获取多文件列表
if not files or all(f.filename == '' for f in files):
flash('请至少选择一个文件')
return redirect(request.url)
saved_urls = []
for file in files:
if file and allowed_file(file.filename):
# 生成安全的文件名,并避免冲突
filename = secure_filename(file.filename)
# 如果文件已存在,添加时间戳或随机数
save_path = app.config['UPLOAD_FOLDER'] / filename
if save_path.exists():
name, ext = os.path.splitext(filename)
import time
filename = f"{name}_{int(time.time())}{ext}"
save_path = app.config['UPLOAD_FOLDER'] / filename
file.save(str(save_path))
# 生成可访问的 URL 路径
file_url = url_for('static', filename=f'uploads/{filename}')
saved_urls.append(file_url)
else:
flash(f'文件 {file.filename} 类型不被支持,已跳过')
if saved_urls:
# 传递图片 URL 列表给模板显示
return render_template('upload.html', images=saved_urls)
else:
flash('没有成功上传任何文件')
return redirect(request.url)
return render_template('upload.html', images=[])
if __name__ == '__main__':
app.run(debug=True)关键点说明:
使用
request.files.getlist('images')获取名为images的表单字段中的所有文件。secure_filename函数来自werkzeug,可以过滤掉文件名中的特殊字符,防止路径遍历攻击。文件保存到
static/uploads/目录后,通过url_for('static', ...)生成可访问的 URL。为了提高安全性,我们校验了文件扩展名,并限制了最大上传大小。
前端模板
在 templates/upload.html 中,构建一个简单的表单,支持多文件选择,并显示上传后的图片列表。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多图片上传与显示</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.container { max-width: 800px; margin: 0 auto; }
.gallery { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 20px; }
.gallery img { width: 200px; height: 150px; object-fit: cover; border: 1px solid #ccc; border-radius: 4px; }
.flash-messages { color: red; list-style: none; padding: 0; }
</style>
</head>
<body>
<div class="container">
<h1>上传多张图片</h1>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="images" multiple accept="image/*">
<button type="submit">上传</button>
</form>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flash-messages">
{% for msg in messages %}
<li>{{ msg }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{% if images %}
<h2>已上传图片</h2>
<div class="gallery">
{% for url in images %}
<img src="{{ url }}" alt="Uploaded image">
{% endfor %}
</div>
{% endif %}
</div>
</body>
</html>模板说明:
<input type="file" name="images" multiple accept="image/*">中的multiple属性允许用户选择多个文件,accept="image/*"限制只能选择图片文件(浏览器层面过滤)。表单的
enctype="multipart/form-data"是文件上传必需的。通过 Jinja2 模板引擎循环
images列表,动态生成<img>标签展示图片。使用
flash消息提示上传结果(如文件类型错误、无文件选择等)。
运行与测试
确保已安装 Flask 和 Werkzeug(通常包含于 Flask 中)。在项目根目录运行 python app.py,打开浏览器访问 http://127.0.0.1:5000/。选择一个或多个图片文件,点击上传按钮,页面下方会显示刚上传的图片缩略图。
增强用户体验:实现客户端预览
如果希望在用户选择图片后立即预览(无需先上传到服务器),可以使用 FileReader API 实现。但本文仅聚焦在“上传后显示”场景。读者可自行扩展。
安全性注意事项
文件类型验证:除了前端限制,后端必须再次验证文件的扩展名(或 MIME 类型)。上述代码使用了扩展名白名单。
文件名过滤:使用
secure_filename防止路径穿越和特殊字符。文件大小限制:通过
MAX_CONTENT_LENGTH限制请求体大小。存储路径:不要将用户上传的文件保存在根目录或具有执行权限的目录。本示例存放在
static/uploads下,仅作为静态文件提供。覆盖冲突处理:如果文件名已存在,我们添加时间戳避免覆盖。
可能遇到的常见问题
上传后页面没有显示图片:检查
static/uploads路径是否正确,以及 Flask 是否在开发模式下正确提供静态文件。生产环境需要配置静态文件服务。上传报错:413 Request Entity Too Large:适当增加
MAX_CONTENT_LENGTH的值,或在前端也进行限制。图片无法打开:确保文件扩展名与真实格式一致,且上传过程未损坏。
小结
本文从零开始搭建了一个 Flask 多图片上传并显示的完整示例。通过前端表单与后端 Flask 路由的配合,用户可以选择多张图片上传,并立即在网页中看到上传后的结果。代码结构清晰,并且考虑了文件名安全、文件类型校验等基础安全措施。读者可以根据实际需求扩展,例如添加删除功能、使用数据库存储图片路径、或者集成云存储服务等。