跳到主要内容

使用下载组件

Dash File Cache提供了一个工厂实例,以实现一个组件,用来透过callback触发下载事件。

downloader = Downloader(id: str)

以下代码对比了分别使用、和不使用Downloader的实现。这两种实现的效果是等价的。

with_downloader.py
import io

from typing import Optional

import dash
from dash import html
from dash import Input
import dash_file_cache as dfc


app = dash.Dash("demo")
service = dfc.ServiceData(dfc.CachePlain(1))
service.serve(app)

downloader = dfc.Downloader("downloader", None)

app.layout = html.Div(
(
html.Div(html.Button(id="btn", children="Download")),
downloader.layout()
)
)

downloader.use_callbacks(app)


@app.callback(downloader.as_output, Input("btn", "n_clicks"))
def a_callback_creating_data(n_clicks: Optional[int]) -> str:
if not n_clicks:
return dash.no_update
address = service.register(
fobj=io.StringIO("test file data..."),
file_name="test.txt",
mime_type="text/plain",
one_time_service=True,
download=True,
)
return address


if __name__ == "__main__":
app.run()

Downloader(...)本身并非一个dash组件。不过,调用downloader.layout(),将会返回与该实例相关的dash组件。而download.use_callbacks(app)提供了用来触发下载事件的callback。

使用Downloader(...)可分为三步:

  1. 透过downloader.layout()将组件添加到dashboard。
  2. 透过downloader.use_callbacks(app)绑定callback。该callback将来自dash.Output(...)的信号转换成了下载事件。
  3. 定义一个callback,其中输出信号定义为downloader.as_output。该输出信号的值需要定义为将要下载的文件对应的地址。这样的地址可以透过ServiceData(...)获取。

将文件注册到缓存时,切记指定file_name的值,并且标记download=True。这些设置确保了动态生成的链接必定会触发下载事件,且所下载文件的文件名由file_name指定。

使用该下载组件的理由

直到dash==2.18.xdcc.Download组件仍然直接作用于字节串数据。这意味着,数据会先在服务端编码成字节串、然后发送到客户端(浏览器)。换言之,dcc.send_file不是flask.send_file的封装。它无法处理大体量的数据。

相对地,这里提供的解决方案,是基于flask.stream_with_context的。下载组件直接访问可下载数据的地址、从而确保了与任意大小的数据兼容。

信息

在未来的版本,将会提供一个真正的纯dash组件,其接口与dcc.Download和本扩展包的ServiceData完全兼容。该组件会作为下载文件的最终解决方案。