plotly/dash-core-components

Official download component

chriddyp opened this issue ยท 9 comments

There are a few ways that you can download content with Dash:

  1. href URI links: https://community.plot.ly/t/download-raw-data/4700/21
  2. Static URLs with URL parameters: https://community.plot.ly/t/allowing-users-to-download-csv-on-click/5550/12

The former apparently doesn't work in IE11 (plotly/dash-recipes#11) and the latter is a little bit out of the Dash framework (URL parameters instead of just inputs).

So, maybe there is a case for a proper download component.

We could possible draw inspiration from here: https://github.com/axetroy/react-download

I've also used Flask functionality for this.

To serve static files:

import os
import dash
from flask import send_from_directory

app = dash.Dash()
STATIC_PATH = os.path.join(app.server.root_path, 'static')
# define layout...
#  add links with, for example: href='/static/test.csv'

@app.server.route('/static/<filename>')
def serve_static(filename):
    return send_from_directory(STATIC_PATH, filename)

Serving dynamic files is also possible with Flask: https://matthewmoisen.com/blog/how-to-download-a-csv-file-in-flask/, which looks similar to URI links, but supports older web browsers

ieipi commented

+1 for the feature.

Hi. Is it is implemented already now? @chriddyp

Dash has a default static files folder "assets" so any files there can be linked to as "assets/FILENAME.X" so you can use that for e.g. CSS files and downloadable static files. For a downloadable static file just add a link on the page.

Dynamic file downloads still requires an URI link creation.

Thannk you @amarvin.

I made an unofficial Download component, and i have been considering how it could be merged into Dash. The syntax is currently like this,

import dash
import dash_html_components as html
from dash.dependencies import Output, Input
from dash_extensions import Download

app = dash.Dash(prevent_initial_callbacks=True)
app.layout = html.Div([html.Button("Download", id="btn"), Download(id="download")])

@app.callback(Output("download", "data"), [Input("btn", "n_clicks")])
def func(n_clicks):
    return dict(content="Hello world!", filename="hello.txt")

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

Is there any guide to how the syntax should be in Dash? Any comments/ideas?

To enable easy download of common objects such as files or dataframes, i had to add a few utility functions (written in Python). Is there anywhere that these could be put in Dash? Or should they stay in a separate utility library? Here is an example for a dataframe,

import dash
import pandas as pd
import dash_html_components as html

from dash.dependencies import Output, Input
from dash_extensions import Download
from dash_extensions.snippets import send_data_frame

# Example data.
df = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [2, 1, 5, 6], 'c': ['x', 'x', 'y', 'y']})
# Create app.
app = dash.Dash(prevent_initial_callbacks=True)
app.layout = html.Div([html.Button("Download", id="btn"), Download(id="download")])

@app.callback(Output("download", "data"), [Input("btn", "n_clicks")])
def func(n_nlicks):
    return send_data_frame(df.to_excel, "mydf.xls")

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

I made an unofficial Download component, and i have been considering how it could be merged into Dash.

Great! When you're ready, make a PR to dash_core_components.

Is there any guide to how the syntax should be in Dash? Any comments/ideas?

The above syntax looks good to me, but we would discuss details in the PR review.

i had to add a few utility functions (written in Python). Is there anywhere that these could be put in Dash?

Assuming they're fairly general-purpose (which df-to-excel certainly would be!) these can go in dash_core_components as well, so that they're available to everyone who has the Download component. See https://github.com/plotly/dash-table/blob/dev/dash_table_base/Format.py as an example of how this could work.