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

如何正确传递数据数组至服务器与下载链接?XLSX导出异常排查

问题描述

需要实现从数据库导出XLSX格式数据的功能,支持用户选择导出列(columns变量),并通过year、station、indicators进行多条件筛选。目前点击下载按钮后,仅能下载包含硬编码表头的空表格,推测需要动态设置表头,但不确定其他问题所在。

相关代码

HTML部分

<tbody class="table-hover">

<table class="table-fill">
  <thead>
    <tr>
      {% for column in columns %}
      <th>{{ column }}</th>
      {% endfor %}
    </tr>
  </thead>

{% for row in rows %}
    <tr>
      {% for column in columns %}
      <td>{{ row[loop.index0] }}</td>
      {% endfor %}
    </tr>
{% endfor %}
    </tbody>
  </table>
</body>
<a href = "{{url_for('download', columns = request.args.getlist('columns'), year = request.args.getlist('year'), indicators=request.args.getlist('indicators'), station=request.args.getlist('station'))}}">download</a>

app.py部分

@app.route("/download", methods=["GET","POST"])
def download():
    if request.method == "GET":
        year = request.form.getlist("year")
        year = [int(i) for i in request.form.getlist("year") if i.isdigit()]
        station = request.form.getlist("station")
        station = [str(i) for i in request.form.getlist("station")]
        indicators = request.form.getlist("indicators")
        indicators = [str(i) for i in request.form.getlist("indicators")]
        columns = request.form.getlist("columns")
        columns = [str(i) for i in request.form.getlist("columns")]
        cur = conn.cursor()
        cur.execute("SELECT {} FROM fact_indicators WHERE fact_year = ANY(%s) AND fact_indicators = ANY(%s) AND fact_station_name = ANY(%s);"
                    .format(",".join(["\"{}\"".format(c) for c in columns])),(year, indicators, station))
        rows = cur.fetchall()
        cur.close
        filename = "fact_T2.xlsx"
        filepath = os.path.join(os.path.dirname(__file__), filename)
        workbook = openpyxl.Workbook()
        worksheet = workbook.active
        worksheet.append([year, station, january, fabruary, may, 1kv, 2kv, 3kv, 4kv])
        for row in rows:
            worksheet.append(list(row))           
        workbook.save(filepath)
        return send_from_directory(os.path.dirname(__file__),  path=filename, as_attachment=True)
    else:
        return redirect(url_for("index"))
问题分析与修复方案

1. GET请求参数获取错误

下载链接是GET请求,参数通过request.args传递,但后端错误使用request.form.getlist获取参数,导致所有筛选条件和列选择都为空,查不到数据。同时存在重复赋值的冗余代码,需修正:

# 修正参数获取方式
year = [int(i) for i in request.args.getlist("year") if i.isdigit()]
station = request.args.getlist("station")
indicators = request.args.getlist("indicators")
columns = request.args.getlist("columns")

2. 硬编码表头替换为动态表头

原代码表头是写死的固定值,需替换为用户选择的columns列表:

# 移除硬编码表头,改用用户选择的columns
worksheet.append(columns)

3. 数据库连接未正确关闭

cur.close是方法调用,需加括号才能执行关闭操作:

cur.close()

4. 潜在SQL注入风险

直接通过format拼接列名存在注入风险,建议先校验columns是否在允许的列名单中,比如:

# 定义允许导出的列
ALLOWED_COLUMNS = ["fact_year", "fact_station_name", "fact_indicators", "january", "fabruary", "may", "1kv", "2kv", "3kv", "4kv"]
# 过滤非法列
valid_columns = [c for c in columns if c in ALLOWED_COLUMNS]
# 拼接SQL时使用校验后的列
cur.execute("SELECT {} FROM fact_indicators WHERE fact_year = ANY(%s) AND fact_indicators = ANY(%s) AND fact_station_name = ANY(%s);"
            .format(",".join([f'"{c}"' for c in valid_columns])),(year, indicators, station))

5. 文件覆盖问题

每次导出使用相同文件名会覆盖之前的文件,建议添加时间戳区分:

from datetime import datetime
# 生成带时间戳的文件名
filename = f"fact_T2_{datetime.now().strftime('%Y%m%d%H%M%S')}.xlsx"
修正后的完整app.py代码
from datetime import datetime
import os
from flask import request, send_from_directory, redirect, url_for
import openpyxl

@app.route("/download", methods=["GET","POST"])
def download():
    if request.method == "GET":
        # 修正:从request.args获取GET请求参数
        year = [int(i) for i in request.args.getlist("year") if i.isdigit()]
        station = request.args.getlist("station")
        indicators = request.args.getlist("indicators")
        columns = request.args.getlist("columns")
        
        # 可选:校验列名合法性,防止SQL注入
        ALLOWED_COLUMNS = ["fact_year", "fact_station_name", "fact_indicators", "january", "fabruary", "may", "1kv", "2kv", "3kv", "4kv"]
        valid_columns = [c for c in columns if c in ALLOWED_COLUMNS]
        if not valid_columns:
            return "请选择合法的导出列", 400
        
        cur = conn.cursor()
        # 使用校验后的列名拼接SQL
        cur.execute("SELECT {} FROM fact_indicators WHERE fact_year = ANY(%s) AND fact_indicators = ANY(%s) AND fact_station_name = ANY(%s);"
                    .format(",".join([f'"{c}"' for c in valid_columns])),
                    (year, indicators, station))
        rows = cur.fetchall()
        # 修正:正确关闭游标
        cur.close()
        
        # 修正:生成带时间戳的文件名,避免覆盖
        filename = f"fact_T2_{datetime.now().strftime('%Y%m%d%H%M%S')}.xlsx"
        filepath = os.path.join(os.path.dirname(__file__), filename)
        
        workbook = openpyxl.Workbook()
        worksheet = workbook.active
        # 修正:使用用户选择的列作为表头
        worksheet.append(valid_columns)
        
        for row in rows:
            worksheet.append(list(row))           
        workbook.save(filepath)
        
        return send_from_directory(os.path.dirname(__file__), path=filename, as_attachment=True)
    else:
        return redirect(url_for("index"))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 07:17:05