Allow users to dowload an Excel in a click

#1

Hi,
Is there an easy way to allow users to download an Excel? I tried to put:

 html.A("Download Excel", href="test.xlsx", download="test.xlsx")

but what I get in the dowloaded document is actually HTML code trying load the page.

If i put:

  html.A("Download Excel", href="test.xlsx")

and I click the component in the page it reloads itself but with /test.xlsx. However, in a normal .html document when I write

 <a href="test.xlsx"> Download Excel </a>

I get the Excel document properly.

Any ideas?

Note: the test.xlsx is in the same directory as the dash script

Download Raw Data - xls/xlsx
#2

See Allowing users to download CSV on click

1 Like
#3

Thx! It works like a charm!

For other people that may be interested this is what I did (Im converting a DataFrame into an Excel):

import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import dash_table_experiments as dte
from flask import send_file
import io
import flask
import pandas as pd


app = dash.Dash()
app.layout = html.Div(children=[
    html.A("download excel", href="/download_excel/"),
])


@app.server.route('/download_excel/')
def download_excel():
    #Create DF
    d = {'col1': [1, 2], 'col2': [3, 4]}
    df = pd.DataFrame(data=d)

    #Convert DF
    strIO = io.BytesIO()
    excel_writer = pd.ExcelWriter(strIO, engine="xlsxwriter")
    df.to_excel(excel_writer, sheet_name="sheet1")
    excel_writer.save()
    excel_data = strIO.getvalue()
    strIO.seek(0)

    return send_file(strIO,
                     attachment_filename='test.xlsx',
                     as_attachment=True)

if __name__ == '__main__':
    app.run_server(debug=True)
7 Likes
#4

@Hujikol0 I was trying to do something like this but I kept getting stuck and I couldn’t find ANYTHING on how to fix the issues I was running into, but your code was FINALLY just what I needed to get my app working. Thank you so much for sharing!!!

#5

Do you know if there would be any reason why this solution wouldn’t work on a mac? I’m trying to run your code but the xls file that gets downloaded has 0 bytes and cannot be opened… :frowning:

#6

This is really great. Thanks for sharing - just what I needed!

#7

Hi - have you tried in different browsers? We have seen strangely different behaviour between Safari and Chrome.

#8

I figured it out, it’s because of the cache. Reset your cache in Chrome and in the future the send_file function you should set the cache_timeout=0.

Here’s the whole example:

import dash
import dash_html_components as html
from flask import send_file
import pandas as pd


app = dash.Dash()
app.layout = html.Div(
    children=[html.A("download excel", href="/download_excel/")])


@app.server.route("/download_excel/")
def download_excel():
    # Create DF
    d = {"col1": [1, 2], "col2": [3, 4]}
    df = pd.DataFrame(data=d)

    # Convert DF
    buf = io.BytesIO()
    excel_writer = pd.ExcelWriter(buf, engine="xlsxwriter")
    df.to_excel(excel_writer, sheet_name="sheet1")
    excel_writer.save()
    excel_data = buf.getvalue()
    buf.seek(0)

    return send_file(
        buf,
        mimetype = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 
        attachment_filename="test11311.xlsx",
        as_attachment=True,
        cache_timeout=0
    )


if __name__ == "__main__":
    app.run_server(debug=True)```
1 Like
#9

Thanks, that’s good to know - we’ll try using that too!

#10

Is it possible to integrate this in a callback which creates the dataframe dynamically (based on inputs to the callback)?

#11

You could refer to Allowing users to download CSV on click to integrate this in a callback.
Hope the following example helps

import dash
import flask
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
from flask import send_file
import io
import pandas as pd

d = {"col1": [1, 2], "col2": [3, 4]}
df = pd.DataFrame(data=d)

app = dash.Dash()
app.layout = html.Div([
    html.A("Download as Excel", id = 'my-link'),
    dcc.Dropdown(
                    id='my-dropdown',
                    options=[
                        {'label': '1', 'value': '1'},
                        {'label': '2', 'value': '2'},
                    ],value='1'
                    )])


@app.callback(
    Output('my-link', 'href'),
    [Input('my-dropdown', 'value')])

def update_link(value):
    return '/dash/urlToDownload?value={}'.format(value)

@app.server.route('/dash/urlToDownload')
def download_excel():
    value = flask.request.args.get('value')
    df1 = df['col{}'.format(value)]
    buf = io.BytesIO()
    excel_writer = pd.ExcelWriter(buf, engine="xlsxwriter")
    df1.to_excel(excel_writer, sheet_name="sheet1", index=False)
    excel_writer.save()
    excel_data = buf.getvalue()
    buf.seek(0)
    return send_file(
        buf,
        mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        attachment_filename="test11311.xlsx",
        as_attachment=True,
        cache_timeout=0
    )


if __name__ == "__main__":
    app.run_server(debug=False)
1 Like
#12

Could you recommend how I can ‘send’ a DataFrame generated in another callback to the download route served by Flask?

2 Likes
#13

You could try to cache it the Dataframe and then pull it in your download_excel().

Also…I’m having problems with running some of the earlier code. I keep getting an error in excel about corrupt values or file format. Any ideas?