1

I would like to upload a csv file using the Dash Upload Component (see Example from Dash Documentation) and post it to a file-server. I want it to work as simple as this working Postman csv Post-Request. My callback currently looks like this:

@app.callback(
Output(component_id="eventlog-dropdown", component_property="options"),
[Input(component_id="upload-data", component_property="contents"),
 Input(component_id="upload-data", component_property="filename")]
)
def upload_eventlog(uploaded_contents, uploaded_filename):
    if uploaded_contents is not None:
        eventlog_data = [
            parse_contents(c, n) for c, n in
            zip(uploaded_contents, uploaded_filename)]

        files = {'file': (uploaded_filename[0], str(eventlog_data))}

        print(files)

        requests.post(MIDDLEWARE_ENDPOINT+"data/upload-eventlog", files=files)

        #Updates the dropdown "eventlog-dropdown"
        files = requests.get(MIDDLEWARE_ENDPOINT + 'data/geteventloglist').json()
        options = [{'label': i, 'value': i} for i in files]
        return options
    elif uploaded_contents is None:
        files = requests.get(MIDDLEWARE_ENDPOINT + 'data/geteventloglist').json()
        options = [{'label': i, 'value': i} for i in files]
        return options

The parse contents function looks like this:

def parse_contents(contents, filename):
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')))
        return df
    elif 'xls' in filename:
        # Assume that the user uploaded an excel file
        df = pd.read_excel(io.BytesIO(decoded))
        return df
except Exception as e:
    print(e)
    return html.Div([
        'There was an error processing this file.'
    ])

The post 'works'. It posts the csv-file and the csv-file is getting stored on the file-server. However, the csv is not comma-seperated anymore and truncated:

69  FN322        Landing  2019-05-22 04:35         Berlin       12:20      4           12.333333
70  FN322  Baggage claim  2019-05-22 03:05         Berlin       01:40      5            1.666667

[71 rows x 7 columns]]

Do you guys know any easy way to post the uploaded filename and contents within the Dash Upload component like the way Postman does it? Or how I could at least save all of the file content?

Thank you very much in advance and have a nice day!

1 Answer 1

2

I figured it out. It was because of the following lines of code:

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

To upload the csv file, we do not want it to be a text stream. It needs to be a byte-like object. The code:

decoded = base64.b64decode(content_string)

already returns a bytes-object such that we only need to return that object. So, the parse_contents() function now looks like this:

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

    decoded = base64.b64decode(content_string)

    try:
        if 'csv' in filename:
            # Assume that the user uploaded a CSV file
            return decoded
     except Exception as e:
        return html.Div([
        'There was an error processing this file.'
    ])

Note that I'm currently only interested in csv-files. That is why I removed the xlsx part. Unfortunately, I do not know whether the code also works for xlsx-files. The code

eventlog_data = [
        parse_contents(c, n) for c, n in
        zip(uploaded_contents, uploaded_filename)]

defines a list with the decoded bytes-object being the first element within that list. So the files-argument of our post-request has to take the first element of out eventlog-data in order to send the byte-data:

files = {'file': (uploaded_filename[0], eventlog_data[0])}

The final callback looks like this:

@app.callback(
Output(component_id="eventlog-dropdown", component_property="options"),
[Input(component_id="upload-data", component_property="contents"),
 Input(component_id="upload-data", component_property="filename")]
)
def upload_eventlog(uploaded_contents, uploaded_filename):
    if uploaded_contents is not None:
        eventlog_data = [
            parse_contents(c, n) for c, n in
            zip(uploaded_contents, uploaded_filename)]
        files = {'file': (uploaded_filename[0], eventlog_data[0])}
        requests.post(MIDDLEWARE_ENDPOINT+"predictivemonitor/upload-eventlog", files=files)

        #Updates the dropdown "eventlog-dropdown"
        files = requests.get(MIDDLEWARE_ENDPOINT + 'predictivemonitor/geteventloglist').json()
        options = [{'label': i, 'value': i} for i in files]
        return options
    elif uploaded_contents is None:
        #Still updates the dropdown "eventlog-dropdown" even if the content is empty
        files = requests.get(MIDDLEWARE_ENDPOINT + 'predictivemonitor/geteventloglist').json()
        options = [{'label': i, 'value': i} for i in files]
        return options
Sign up to request clarification or add additional context in comments.

1 Comment

Hi MoDo, I am trying to upload and read a csv file to subsequently use it as a dataframe for my other operations, but I am not able to do it. Any way you can help?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.