Skip to main content
Version: 0.2.0

Example of the downloader

Example of using the downloader
Example of the downloader

Check the following link to review the demo of downloading a file.

download_file.py

info

Running the exmaple requires users to install the optional dependencies by

python -m pip install dash[diskcache]

This small demo only provides one Start button.

  1. By clicking it, a short waiting period will be simulated and the progress of the callback will be displayed.
  2. When the callback gets finalized, the cache type and the address of the file to be downloaded will be displayed and the downloading event will automatically start.
  3. When the downloading event is finalized successfully or canceled by users, a status will be displayed.

Define the cache and the downloader

In the initialization method, we provide the ServiceData(...) handle.

class Demo:
def __init__(self) -> None:
self.service = ServiceData(CacheFile(None))
self.root = os.path.dirname(__file__)

To simulate the process of preparing a large file, we store the data with CacheFile(...) which is compatible with the background callback.

Define the layout

The implementation of the layout contains one button and two outputs.

html.Div(
(
html.Div(
html.P(
(
html.Span(
"Download a image:", style={"paddingRight": "0.5rem"}
),
html.Button(id="btn", children="Start"),
)
)
),
html.Div((html.P(("Progress:", html.Span(id="prog"))))),
html.Div((html.P("Cache type:"), html.P(id="type"))),
html.Div((html.P("Cache address:"), html.P(id="addr"))),
html.Div((html.P("Downloader status:"), html.P(id="dlstats"))),
Downloader(id="download", mitm="/dfc-downloader"),
),
)

The downloader is specified here. It is a Dash component. The property mitm is a monitor preserving the active status of the downloading event. Its service route need to be the same as the configuration of ServiceDownloader which is used later.

Similar to the other examples, here we only use one button with id=btn to start the callback. The returned value of the callback will be handled by the Downloader component.

Define the callbacks

In this example, we use the background callback to display the progress. We deliberately make the callback wait for a short period to simulate the process of preparing the large-size data.

@app.callback(
Output("type", "children"),
Output("addr", "children"),
Input("btn", "n_clicks"),
background=True,
running=[
(Output("btn", "disabled"), True, False),
],
progress=[Output("prog", "children")],
manager=background_callback_manager,
prevent_initial_call=True,
)
def click_get_image(
set_progress: Callable[[Tuple[str]], None], n_clicks: Optional[int]
):
if not n_clicks:
return dash.no_update, dash.no_update
file_path = os.path.join(self.root, "test_image.svg")
n = 10
for i in range(n):
time.sleep(0.1)

set_progress(("{0}%".format(int(round((i + 1) / n * 100))),))
addr = self.service.register(
file_path,
content_type="image/svg+xml; charset=utf-8",
mime_type="image/svg+xml",
one_time_service=True,
download=True,
)
return str(file_path.__class__.__name__), addr

The second output is the address of the cached data. This callback has two special configurations. The first configuration is the content_type, where we provide the charset of the text file. The second configuration is download=True. If the file is cached for downloading, this argument should be always specified as True.

The returned address will be used to fire another callback subsequently, the definition of the next callback is:

@app.callback(
Output("download", "url"),
Input("addr", "children"),
prevent_initial_call=True,
)
def trigger_downloading_event(addr):
if not addr:
return dash.no_update
return addr

This callback triggers the downloading event. When the address is valid, it will be used for updating the property url of the downloader. Note that every time when url is updated, a downloading event will start.

note

Even if the empty address is input to the component, it is still OK because an empty url will not trigger a downloading event.

@app.callback(
Output("dlstats", "children"),
Input("download", "status"),
prevent_initial_call=True,
)
def trigger_downloading_status(status: Optional[DownloaderStatus]):
if not status:
return ""
return str(status)

The last callback is used for showing the status of the downloader. As long as the status is updated by once, no matter whether the downloading is successful or not, the status property will be updated as a dictionary.

note

The property status is maintained by the downloader itself. Modifying this property by a callback is meaningless.

Load the services

For a service handle, its configurations are determined after the initialization. Therefore, it is OK to call service.serve(...) method anywhere. In this example, we make the services loaded when calling the load() method of the WrappedApp.

class WrappedApp:
"""A wrapped app. This wrapped app is accessed by the tests."""

def __init__(self, app: dash.Dash) -> None:
self.app = app

def load(self) -> None:
demo = Demo()
app.layout = demo.layout()
demo.bind(app)
demo.service.serve(app)
ServiceDownloader("/dfc-downloader").serve(app)

The route of ServiceDownloader needs to be consistent with the mitm property of the downloader.

warning

Configuring this feature will make the mitm served by the local script. If not make these configurations, the mitm will be hosted by a script on GitHub. In other words, if users let mitm be empty and do not configure ServiceDownloader, the downloader will work only when the Internet is accessible.