如何修改Flask多文件上传代码 将上传文件读取为脚本可用变量
实现方案
你现有代码的核心问题有两个:一是前端用两个独立表单,一次请求只能上传1个文件,HTTP无状态特性会导致第一次上传的文件在第二次请求时无法被读取;二是后端拿到文件后仅做了磁盘存储,没有读取内容对接原有处理逻辑。按以下步骤修改即可:
1. 修改前端模板button.html
把两个文件选择框合并到同一个表单,给两个文件输入框设置不同的name属性,实现一次提交同时上传两个文件:
<!doctype html> <title>数据处理文件上传</title> <h2>上传待处理文件</h2> <form method="post" action="/" enctype="multipart/form-data"> <dl> <p> <label>第一个文件:</label> <input type="file" name="file1" autocomplete="off" required accept=".tpi"> </p> <p> <label>第二个文件:</label> <input type="file" name="file2" autocomplete="off" required accept=".tpi"> </p> </dl> <p> <input type="submit" value="开始处理并下载CSV"> </p> </form> {% with messages = get_flashed_messages() %} {% if messages %} <ul class=flashes> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %}
说明:补充了原模板缺失的flash消息渲染逻辑,上传报错用户可直接看到提示;加了accept属性限制前端只能选tpi文件,减少非法上传。
2. 修改后端upload.py逻辑
直接读取上传文件的内容,无缝对接你原来的数据处理代码,处理完成后直接返回CSV文件供下载,不需要额外存临时文件:
import os import pandas as pd from flask import Flask, flash, request, redirect, render_template, make_response from werkzeug.utils import secure_filename app = Flask(__name__) app.secret_key = "随便填一个随机字符串即可" # flash功能必须配置密钥 ALLOWED_EXTENSIONS = {'tpi'} def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS # 原有数据处理函数,不需要做任何修改 def parse_value(txt): reclines = [] for line in txt.split('\n'): if ':' not in line: if reclines: yield reclines reclines = [] else: reclines.append(line) def parse_fields(reclines): res = {} for line in reclines: key, val = line.strip().rstrip(',').split(':', 1) res[key.strip()] = val.strip() return res @app.route('/', methods=['GET']) def upload_form(): return render_template('button.html') @app.route('/', methods=['POST']) def upload_file(): # 校验两个文件是否都上传 if 'file1' not in request.files or 'file2' not in request.files: flash('请同时上传两个文件') return redirect(request.url) f1 = request.files['file1'] f2 = request.files['file2'] # 校验文件名非空 if f1.filename == '' or f2.filename == '': flash('请选择两个待上传的文件') return redirect(request.url) # 校验文件格式 if not (allowed_file(f1.filename) and allowed_file(f2.filename)): flash('仅允许上传tpi格式文件') return redirect(request.url) # 直接读取文件内容,和本地open后read的结果完全一致 # 如果你的文件是gbk编码,把utf-8换成gbk即可 content = f1.read().decode('utf-8') content2 = f2.read().decode('utf-8') # 原有数据处理逻辑,不需要修改 res = [] for rec in parse_value(content): res.append(parse_fields(rec)) res2 = [] for rec in parse_value(content2): res2.append(parse_fields(rec)) df = pd.json_normalize(res) df2 = pd.json_normalize(res2) # 替换成你自己的两个DataFrame映射匹配逻辑 # 示例为简单拼接,按实际业务修改即可 final_df = pd.concat([df, df2], axis=1) # 生成CSV并返回给用户下载 output = make_response(final_df.to_csv(index=False, encoding='utf-8-sig')) output.headers["Content-Disposition"] = "attachment; filename=result.csv" output.headers["Content-type"] = "text/csv" return output if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True)
关键说明
- 不需要把上传的文件先存到服务器磁盘再读取,Flask返回的文件对象是类文件对象,直接调用
read()方法就能拿到内容,和你原来本地打开文件的读取结果完全兼容,原有解析函数零修改即可复用。 - 不要用两个独立表单分两次上传:HTTP协议是无状态的,第一次请求上传的文件不会自动保留到第二次请求,要么用session/临时文件存储第一次上传的内容,要么像示例一样一次传两个,后者实现最简单、用户体验更好。
- 如果你的tpi文件不是utf-8编码,把
decode('utf-8')和CSV导出的encoding参数改成对应编码即可,避免中文乱码。
内容的提问来源于stack exchange,提问作者gcoder
相关产品推荐
相关产品推荐

