How can I setup two dcc.Upload components and have two separate callbacks

I have modified the dcc.Upload example at Upload | Dash for Python Documentation | Plotly

The original one works fine, but as soon as I insert one more instance of dcc.Upload so I can have second upload box, and setup its individual callback, both dcc.Uploads stop working. They are not even called anymore.

For example, from screenshot below, I have two dcc.Uploads setup.

upload = dcc.Upload(
id=ā€˜upload-dataā€™,
ā€¦
ā€¦

upload1 = dcc.Upload(
id=ā€˜upload-data1ā€™,
ā€¦
ā€¦

I have the original callback setup as it is in the example

@app.callback([Output('output-data-upload', 'children'),
              Output('file1', 'children')],
              [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 = [
            parse_contents(c, n, d) for c, n, d in
            zip(list_of_contents, list_of_names, list_of_dates)]
        return [children, list_of_names]

    #Need to add default return statement in case if condition is not
    #executed or true. 
    return dash.no_update, dash.no_update

But as soon as I setup upload1 callback, both of them stop working. I end up deleting call to update_output1, then the code works for the original dcc.upload.

In short, how can I have two dcc.uploads setup. I am not sure what I am doing wrong in my callbacks. I do not see any errors either in my log. The second callback for the second dcc.uploader is not even called. The entire code is below.

@app.callback([Output('output-data-upload', 'children'),
              Output('file2', 'children')],
              [Input('upload-data1', 'contents')],
              [State('upload-data1', 'filename'),
              State('upload-data1', 'last_modified')])
def update_output1(list_of_contents, list_of_names, list_of_dates):
    if list_of_contents is not None:
        children = [
            parse_contents(c, n, d) for c, n, d in
            zip(list_of_contents, list_of_names, list_of_dates)]
        return [children, list_of_names]

    #Need to add default return statement in case if condition is not
    #executed or true. 
    return dash.no_update, dash.no_update
#Need to add default return statement in case if condition is not
#executed or true. 
return dash.no_update, dash.no_update 

#Updated March 4, 2025

import dash
import pandas as pd
import json
import dash_html_components as html
import plotly.graph_objects as go
import dash_core_components as dcc
import plotly.express as px
from dash.dependencies import Input, Output, State
from datetime import datetime as dt
import dash_table

import base64
import datetime
import io
import dash_bootstrap_components as dbc

app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
app.config['suppress_callback_exceptions'] = True

# the styles for the main content position it to the right of the sidebar and
# add some padding.
CONTENT_STYLE = {
    "margin-left": "1rem",
    "margin-right": "1rem",
    "padding": "3rem 1rem",
}


upload = 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'
        },
        # Allow multiple files to be uploaded
        multiple=True
    )


upload1 = dcc.Upload(
        id='upload-data1',
        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'
        },
        # Allow multiple files to be uploaded
        multiple=True
    )

filesdisplay =  html.Div([dbc.Card(
    [
        html.Div(
            [                
                dbc.Row(
                [
                dbc.Col([
                dbc.Label("File 1 Name", id='file1'),
                ]),
                dbc.Col([
                dbc.Label("File 2 Name", id='file2'),
                ]),
                dbc.Col(html.Button('Submit', id='kbutton', n_clicks=0))
                ]),    
            ]
        ),
    ],
    body=True,
)
])

content = html.Div([
    upload,
    upload1,
    filesdisplay,
    html.Div(id='output-data-upload'),
   
], style=CONTENT_STYLE)

app.layout= html.Div([dcc.Location(id="url"), content])



def parse_contents(contents, filename, date):
    content_type, content_string = contents.split(',')

    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            # Assume that the user uploaded a CSV file
            df = pd.read_csv(
                io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename:
            # Assume that the user uploaded an excel file
            df = pd.read_excel(io.BytesIO(decoded))
        elif 'xml' in filename:
            print("xml file selected")
    except Exception as e:
        print(e)
        return html.Div([
            'There was an error processing this file.'
        ])

    if 'xml' in filename:
        return html.Div(["xml file"])
    else:
        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(),  # horizontal line

            # For debugging, display the raw contents provided by the web browser
            html.Div('Raw Content'),
            html.Pre(contents[0:200] + '...', style={
                'whiteSpace': 'pre-wrap',
                'wordBreak': 'break-all'
                })
            ])

@app.callback([Output('output-data-upload', 'children'),
              Output('file1', 'children')],
              [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 = [
            parse_contents(c, n, d) for c, n, d in
            zip(list_of_contents, list_of_names, list_of_dates)]
        return [children, list_of_names]

    #Need to add default return statement in case if condition is not
    #executed or true. 
    return dash.no_update, dash.no_update


@app.callback([Output('output-data-upload', 'children'),
              Output('file2', 'children')],
              [Input('upload-data1', 'contents')],
              [State('upload-data1', 'filename'),
              State('upload-data1', 'last_modified')])
def update_output1(list_of_contents, list_of_names, list_of_dates):
    if list_of_contents is not None:
        children = [
            parse_contents(c, n, d) for c, n, d in
            zip(list_of_contents, list_of_names, list_of_dates)]
        return [children, list_of_names]

    #Need to add default return statement in case if condition is not
    #executed or true. 
    return dash.no_update, dash.no_update

if __name__ == '__main__': 
    print("Running app.py from ReadingXML 02252025")
    app.run_server()

Hi !
From what I see you have a duplicate callback output exception, because you added ā€œoutput-file-data-uploadā€ as an output twice.

And the fact that you have ā€œsuppress_callback_exceptionsā€ to True is masking this error.

How to fix this : just use a different output for the second upload :slight_smile:

1 Like

Yes thank you! That was the issue. For further information, you can have multiple callbacks to the same component, but you will need to declare that the component has duplicate callbacks, plus declare when initializing app that multiple callbacks are allowed:

app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP], 
                prevent_initial_callbacks="initial_duplicate")

..
..
..
@app.callback([Output('output-data-upload', 'children', allow_duplicate=True),
              Output('file1', 'children', allow_duplicate=True),
              Output('msgconsole', 'value', allow_duplicate=True),
              Output('intermediate-value', 'data')],
              [Input('upload-data1', 'contents')],
              [State('upload-data1', 'filename'),
              State('upload-data1', 'last_modified')])
def update_output1(list_of_contents, list_of_names, list_of_dates):
..
..
..
1 Like