Upload CSV with dcc.Upload to store using dcc.Store and then displaying table

Hey community, haven’t found a good answer to this issue I am having. Please help!

My goal is to upload CSV via dcc.Upload, store it in dcc.Store, and then display table.

I am taking the code from dcc.Upload (here) and inserting a dcc.Store component. The goal is to have two callbacks. The first one would have upload data as input and store as output. The second callback would take store as input and output to a html.Div.

See code below that is not working:

### CODE for dcc.upload HELP

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'
        },
        # Allow multiple files to be uploaded
        multiple=True
    ),
    dcc.Store(id='store'),
    html.Div(id='output-data-upload'),
])


#Goal here is to store the csv from upload into dcc.Store
@app.callback(Output('store', 'data'),
              Input('upload-data', 'contents'),
              State('upload-data', 'filename'),
              State('upload-data', 'last_modified'))

def update_output(contents, list_of_names, list_of_dates):
    content_type, content_string = contents.split(',')

    decoded = base64.b64decode(content_string)

    df = pd.read_csv(io.StringIO(decoded.decode('utf-8')))

    return df.to_json(date_format='iso', orient='split') #I have to convert to json format to store in dcc.Store


#Goal here is to then extract dataframe from dcc.store by reading json and output a table
@app.callback(
    Output('output-data-upload', 'children'),
    Input('store', 'data')
)

def output_from_store(stored_data):
    df = pd.read_json(stored_data, orient='split')

    return html.Div([


        dash_table.DataTable(
            df.to_dict('records'),
            [{'name': i, 'id': i} for i in df.columns]
        ),

        html.Hr(),  # horizontal line


    ])



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

What did you try, which error message do you get?

The problem is that multiple=True causes the dcc.Upload to wrap the uploaded file in a list (as you are implying there could be one or more). When the update_output function is fired, contents.split(',') throws the error due to type(contents) being a list, not a string.

To solve this, you need to:

  1. Remove multiple=True from the dcc.Upload component.
  2. Prevent trying to update when no file is uploaded (such as on initialization) by raising dash.exceptions.PreventUpdate when we have no file.

Here’s the slightly modified solution:

import base64
import io

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

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'
        },
    ),
    dcc.Store(id='store'),
    html.Div(id='output-data-upload'),
])


@app.callback(Output('store', 'data'),
              Input('upload-data', 'contents'),
              State('upload-data', 'filename'),
              State('upload-data', 'last_modified'))
def update_output(contents, list_of_names, list_of_dates):
    if contents is None:
        raise PreventUpdate

    content_type, content_string = contents.split(',')

    decoded = base64.b64decode(content_string)

    df = pd.read_csv(io.StringIO(decoded.decode('utf-8')))

    return df.to_json(date_format='iso', orient='split')


@app.callback(
    Output('output-data-upload', 'children'),
    Input('store', 'data')
)
def output_from_store(stored_data):
    df = pd.read_json(stored_data, orient='split')

    return html.Div([
        dash_table.DataTable(
            df.to_dict('records'),
            [{'name': i, 'id': i} for i in df.columns]
        ),
        html.Hr(),
    ])


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