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

在Plotly Dash中通过Upload组件在函数间传递Pandas DataFrame

在Plotly Dash中上传Excel并共享Pandas DataFrame的正确姿势

问题背景

在Plotly Dash中处理Excel工作簿时,遇到了无法将上传生成的Pandas DataFrame传递给其他函数的问题:

  • 使用全局变量时触发NameError: name 'df' is not defined;
  • 尝试函数间传递但不知道如何在依赖组件输入的parse_contents中正确处理。

错误原因

  1. 全局变量的致命问题:update_output函数在用户未上传文件时就尝试打印df,此时全局df尚未定义,直接抛出错误。而且全局变量在多用户场景下会导致数据串用,完全不符合Dash的设计逻辑。
  2. 返回值类型错误:parse_contents同时返回HTML组件和DataFrame,但Dash的children属性只能接受组件/字符串,混入DataFrame会引发潜在的渲染错误。

解决方案:使用dcc.Store存储数据

Dash内置的dcc.Store是官方推荐的跨回调共享数据的方案,它会把数据存在浏览器端(或服务器端,可配置),安全且符合响应式设计。具体步骤:

  1. 在布局中添加dcc.Store组件保存DataFrame的JSON格式数据;
  2. 修改上传回调,解析文件后将DataFrame转成JSON存入Store;
  3. 其他需要使用DataFrame的函数,通过回调从Store读取并转回DataFrame。

修改后的完整代码

import base64
import datetime
import io

import dash
from dash.dependencies import Input, Output, State
from dash import dcc, html, dash_table

import pandas as pd

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
    dcc.Upload(
        id='upload-data',
        children=html.Div([
            'Drag and Drop or ',
            html.A('Select Files')
        ]),
        style={
            'width': '100%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin': '10px'
        },
        multiple=True
    ),
    html.Div(id='output-data-upload'),
    # 新增dcc.Store存储DataFrame的JSON数据
    dcc.Store(id='stored-dataframe', data={}),
    # 示例:展示DataFrame的基本信息,验证数据传递
    html.Div(id='df-info')
])

def parse_contents(contents, filename, date):
    content_type, content_string = contents.split(',')
    decoded = base64.b64decode(content_string)
    df = None
    try:
        if 'csv' in filename:
            df = pd.read_csv(io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename or 'xlsx' in filename:
            workbook_xl = pd.ExcelFile(io.BytesIO(decoded))
            df = pd.read_excel(workbook_xl, sheet_name=0)
    except Exception as e:
        print(e)
        return html.Div(['文件处理出错']), {}

    # 返回UI组件和DataFrame的JSON格式(orient='split'保留行列结构)
    return html.Div([
        html.H5(filename),
        html.H6(datetime.datetime.fromtimestamp(date)),
        dash_table.DataTable(
            df.to_dict('records'),
            [{'name': i, 'id': i} for i in df.columns]
        ),
        html.Hr(),
        html.Div('原始内容预览'),
        html.Pre(contents[0:200] + '...', style={
            'whiteSpace': 'pre-wrap',
            'wordBreak': 'break-all'
        })
    ]), df.to_json(date_format='iso', orient='split')

@app.callback(
    [Output('output-data-upload', 'children'),
     Output('stored-dataframe', 'data')],
    Input('upload-data', 'contents'),
    State('upload-data', 'filename'),
    State('upload-data', 'last_modified')
)
def update_output(list_of_contents, list_of_names, list_of_dates):
    if list_of_contents is not None:
        children = []
        stored_data = {}
        # 处理多文件上传,这里取最后一个有效文件的DataFrame,可按需调整
        for c, n, d in zip(list_of_contents, list_of_names, list_of_dates):
            component, df_json = parse_contents(c, n, d)
            children.append(component)
            if df_json:
                stored_data = df_json
        return children, stored_data
    return [], {}

# 示例回调:从Store读取DataFrame并用于业务逻辑
@app.callback(
    Output('df-info', 'children'),
    Input('stored-dataframe', 'data')
)
def use_stored_dataframe(stored_data):
    if not stored_data:
        return html.Div('请上传文件查看数据信息')
    # 将JSON转回Pandas DataFrame
    df = pd.read_json(stored_data, orient='split')
    # 这里可以调用任何需要df的自定义函数
    return html.Div([
        html.H4('数据概览'),
        html.P(f'总行数:{df.shape[0]},总列数:{df.shape[1]}'),
        html.P(f'列名:{", ".join(df.columns)}')
    ])

if __name__ == '__main__':
    app.run_server(debug=True)

关键改动说明

  • 新增dcc.Store:安全存储DataFrame的JSON数据,避免全局变量的问题;
  • 调整parse_contents返回值:只返回合法的UI组件和JSON数据,不再混入DataFrame;
  • 多回调协作:上传回调负责解析数据并存储,业务回调从Store读取数据,完全符合Dash的响应式流程;
  • 支持多文件上传:代码中处理了多文件场景,可根据需求调整为存储多个DataFrame。

内容的提问来源于stack exchange,提问作者John Conor

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 05:10:56