从 Dash/Flask 应用程序下载动态生成的文件
Posted
技术标签:
【中文标题】从 Dash/Flask 应用程序下载动态生成的文件【英文标题】:Downloading dynamically generated files from a Dash/Flask app 【发布时间】:2019-06-23 21:46:56 【问题描述】:我尝试构建一个 Dash 应用程序的最小示例,以说明动态生成文件的问题,然后可以通过下载按钮下载该文件。
如果您运行此示例,您将看到一个可以输入文本的文本区域。单击“输入”按钮会将文本存储到文件中并为该文件创建一个下载按钮。
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import uuid
stylesheets = [
"https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css", # Bulma
]
# create app
app = dash.Dash(
__name__,
external_stylesheets=stylesheets
)
app.layout = html.Div(
className="section",
children=[
dcc.Textarea(
id="text-area",
className="textarea",
placeholder='Enter a value...',
style='width': '300px'
),
html.Button(
id="enter-button",
className="button is-large is-outlined",
children=["enter"]
),
html.Div(
id="download-area",
className="block",
children=[]
)
]
)
def build_download_button(uri):
"""Generates a download button for the resource"""
button = html.Form(
action=uri,
method="get",
children=[
html.Button(
className="button",
type="submit",
children=[
"download"
]
)
]
)
return button
@app.callback(
Output("download-area", "children"),
[
Input("enter-button", "n_clicks")
],
[
State("text-area", "value")
]
)
def show_download_button(n_clicks, text):
# turn text area content into file
filename = f"uuid.uuid1().txt"
path = f"downloadable/filename"
with open(path, "w") as file:
file.write(text)
uri = path
return [build_download_button(uri)]
if __name__ == '__main__':
app.run_server(debug=True)
但是,生成的 URI 似乎不正确,因为单击按钮只会重定向到索引页面。需要什么才能让它发挥作用?
【问题讨论】:
【参考方案1】:由于 Dash 是基于 Flask 构建的,flask 无法找到生成的文本文件的 URI。
解决方法是添加flask路由重定向下载资源, 官方plotly dash存储库中有一个简单的例子, https://github.com/plotly/dash-recipes/blob/master/dash-download-file-link-server.py
下面的修改代码解决了你的问题
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import uuid
import os
import flask
stylesheets = [
"https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css", # Bulma
]
# create app
app = dash.Dash(
__name__,
external_stylesheets=stylesheets
)
app.layout = html.Div(
className="section",
children=[
dcc.Textarea(
id="text-area",
className="textarea",
placeholder='Enter a value...',
style='width': '300px'
),
html.Button(
id="enter-button",
className="button is-large is-outlined",
children=["enter"]
),
html.Div(
id="download-area",
className="block",
children=[]
)
]
)
def build_download_button(uri):
"""Generates a download button for the resource"""
button = html.Form(
action=uri,
method="get",
children=[
html.Button(
className="button",
type="submit",
children=[
"download"
]
)
]
)
return button
@app.callback(
Output("download-area", "children"),
[
Input("enter-button", "n_clicks")
],
[
State("text-area", "value")
]
)
def show_download_button(n_clicks, text):
if text == None:
return
# turn text area content into file
filename = f"uuid.uuid1().txt"
path = f"downloadable/filename"
with open(path, "w") as file:
file.write(text)
uri = path
return [build_download_button(uri)]
@app.server.route('/downloadable/<path:path>')
def serve_static(path):
root_dir = os.getcwd()
return flask.send_from_directory(
os.path.join(root_dir, 'downloadable'), path
)
if __name__ == '__main__':
app.run_server(debug=True)
或者,您可以使用static
目录而不是downloadable
目录,它也可以。
更多关于flask静态目录的信息: http://flask.pocoo.org/docs/1.0/tutorial/static/
这里是sn-p,
#your code
def show_download_button(n_clicks, text):
if text == None:
return
filename = f"uuid.uuid1().txt"
path = f"static/filename" # =====> here change the name of the direcotry to point to the static directory
with open(path, "w") as file:
file.write(text)
uri = path
return [build_download_button(uri)]
#your code
【讨论】:
你能解释一下语法“解决办法:
import uuid
import dash
from dash.dependencies import Input, Output, State
import flask
from flask.helpers import send_file
import dash_core_components as dcc
import dash_html_components as html
stylesheets = [
"https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css", # Bulma
]
server = flask.Flask('app')
# create app
app = dash.Dash(
__name__,
external_stylesheets=stylesheets,
server=server # <-- do not forget this line
)
# (...) your code here
@server.route("/downloadable/<path>")
def download_file (path = None):
return send_file("downloadable/" + path, as_attachment=True)
【讨论】:
【参考方案3】:使用 Dash 1.20.0,您现在拥有一个 dcc.Download
组件,用于基于用户的动态下载。它不需要创建自定义按钮,uuid
和 flask.send_file
。
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import uuid
stylesheets = [
"https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css", # Bulma
]
# create app
app = dash.Dash(
__name__,
external_stylesheets=stylesheets
)
app.layout = html.Div(
className="section",
children=[
dcc.Textarea(
id="text-area",
className="textarea",
placeholder='Enter a value...',
style='width': '300px'
),
html.Button("Enter", id="btn_txt"),
dcc.Download(id="download-text")
]
)
@app.callback(
Output("download-text", "data"),
Input("btn_txt", "n_clicks"),
State("text-area", "value"),
prevent_initial_call=True,
)
def create_download_file(n_clicks, text):
filename = "file.txt"
# Alternatively:
# filename = f"uuid.uuid1().txt"
return dict(content=text, filename=filename)
【讨论】:
以上是关于从 Dash/Flask 应用程序下载动态生成的文件的主要内容,如果未能解决你的问题,请参考以下文章