Skip to content

Flask - 文件上传

处理文件上传是 Web 应用程序的一个常见需求。Flask 使这一过程相对简单。

基本流程包括:

  • 一个使用 method="POST" 且关键在于使用 enctype="multipart/form-data" 的 HTML 表单。这种编码允许文件数据与表单一起发送。
  • 表单中用于用户选择文件的 <input type="file" name="some_name"> 元素。
  • 一个处理 POST 请求的 Flask 视图函数。
  • 通过类似字典的 request.files 对象访问上传的文件数据。
  • 将文件安全地保存到服务器上的指定位置。

上传文件时,Flask(使用底层 Werkzeug 库)最初会将其保存到服务器上的临时位置。您的视图函数然后使用 HTML input 标签中指定的 name 属性(例如,request.files['file'])从 request.files 中检索此文件对象。

从 request.files 获得的 文件对象 有一个 save() 方法,您可以使用它来永久保存文件。在保存用户提供的文件名之前对其进行清理(sanitize)至关重要,以防止目录遍历攻击。Werkzeug 提供了 secure_filename() 函数用于此目的。它会从文件名中移除潜在有害的字符(例如 ../)。

您可以配置 Flask 以更有效地管理上传:

配置项描述
app.config['UPLOAD_FOLDER']上传文件应存储的目录路径。
app.config['MAX_CONTENT_LENGTH']允许的最大文件大小,以字节为单位(例如,16 * 1024 * 1024 表示 16 MB)。Flask 将拒绝有效载荷(payload)更大的请求。

让我们创建一个简单的文件上传示例。

首先,是 HTML 表单 (templates/upload.html):

<!doctype html>
<html>
<head>
<title>Upload File</title>
</head>
<body>
<h2>Select a file to upload</h2>
<form action="{{ url_for('handle_upload') }}" method="POST" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="Upload">
</form>
</body>
</html>

此表单将通过 POST 方法把选定的文件发送到与 handle_upload 视图函数关联的 URL。

现在,是 Flask 应用程序 (app.py):

import os
from flask import Flask, render_template, request, redirect, url_for, flash
from werkzeug.utils import secure_filename
# 定义上传文件的路径
UPLOAD_FOLDER = 'uploads'
# 定义允许的文件扩展名(可选,但建议用于安全)
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16 MB limit
app.config['SECRET_KEY'] = 'super-secret-key' # 用于闪现消息
# 确保上传文件夹存在
if not os.path.exists(UPLOAD_FOLDER):
os.makedirs(UPLOAD_FOLDER)
# 检查允许文件扩展名的辅助函数
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/')
def index():
# 重定向到上传表单页面
return redirect(url_for('show_upload_form'))
@app.route('/upload')
def show_upload_form():
# 渲染包含上传表单的页面
return render_template('upload.html')
@app.route('/uploader', methods=['POST'])
def handle_upload():
if 'file' not in request.files:
flash('No file part in the request', 'danger')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file', 'warning')
return redirect(url_for('show_upload_form'))
if file and allowed_file(file.filename):
# 清理文件名
filename = secure_filename(file.filename)
# 创建完整的保存路径
save_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
try:
file.save(save_path)
flash(f'File "{filename}" uploaded successfully!', 'success')
# 您可以重定向到显示已上传文件或成功消息的页面
return redirect(url_for('show_upload_form'))
except Exception as e:
flash(f'An error occurred while saving the file: {e}', 'danger')
return redirect(url_for('show_upload_form'))
elif file and not allowed_file(file.filename):
flash('File type not allowed', 'danger')
return redirect(url_for('show_upload_form'))
else:
# 理论上在上述检查后不会发生,但作为备用
flash('An unknown error occurred', 'danger')
return redirect(url_for('show_upload_form'))
if __name__ == '__main__':
app.run(debug=True)

Python 代码中的关键点:

  • UPLOAD_FOLDER: 定义文件保存的位置。
  • ALLOWED_EXTENSIONS/allowed_file(): 基本验证,仅接受某些文件类型。这对于安全很重要。
  • MAX_CONTENT_LENGTH: 限制上传文件大小。
  • SECRET_KEY: Flask 用于闪现消息所必需。
  • 文件夹创建: 确保上传目录存在。
  • handle_upload() 函数:
  • 检查请求中是否存在文件部分 ('file' in request.files)。
  • 检查是否实际提交了文件名 (file.filename == '')。
  • 使用 allowed_file() 检查文件类型是否允许。
  • 使用 secure_filename() 获取安全的文件名。
  • 使用 os.path.join() 构建完整路径。
  • 调用 file.save() 保存文件。
  • 使用 flash() 向用户提供反馈。
  • 重定向回上传表单页面(或潜在的成功页面)。

要运行此示例,保存代码,创建一个名为 templates 的文件夹并在其中创建 upload.html 文件,然后运行 python app.py。在浏览器中访问 http://localhost:5000/(将重定向到 /upload)。您将看到文件输入框和提交按钮。选择一个文件(注意允许的扩展名和大小限制)并点击“Upload”。如果成功,文件将保存在 uploads 目录中(该目录创建在 app.py 所在文件夹),并显示一条成功消息。

重要安全注意事项:

  • 切勿信任用户输入: 始终验证文件名 (secure_filename) 和文件类型 (allowed_file)。如有必要,考虑更严格的检查,例如检查文件头(魔术数字)。
  • 存储位置: 如果上传的文件不需要直接通过 Web 访问,请避免将其直接保存在 Web 应用程序的公共目录中。考虑使用一个独立的、非 Web 可访问的目录或云存储(例如 AWS S3、Google Cloud Storage)。
  • 资源限制: 设置 MAX_CONTENT_LENGTH 以防止通过巨大上传进行的拒绝服务攻击。
  • 文件服务: 在将上传的文件提供给用户时务必小心,特别是如果它们包含可执行内容或 HTML/JavaScript。设置适当的 Content-Type 头部,并可能设置 Content-Disposition: attachment 以强制下载。
  • 权限: 确保 Web 服务器进程对 UPLOAD_FOLDER 具有写入权限,但在其他地方仅具有最低限度的权限。