如何在多页应用程序上下载带有 plotly-dash 的文件?
How to download a file with plotly-dash on a multi-page app?
我已经知道以下方法 (link here):
server = Flask(__name__)
app = dash.Dash(server=server)
@server.route("/download/<path:path>")
def download(path):
"""Serve a file from the upload directory."""
return send_from_directory(UPLOAD_DIRECTORY, path, as_attachment=True)
但问题是,当我使用 Plotly 建议的多页方法时(link here(低于 "Structuring a Multi-Page App" - index.py
)):
app.layout = html.Div([
dcc.Location(id='url', refresh=False),
html.Div(id='page-content')
])
@app.callback(Output('page-content', 'children'),
[Input('url', 'pathname')])
def display_page(pathname):
if pathname == '/apps/app1':
return app1.layout
elif pathname == '/apps/app2':
return app2.layout
else:
return '404'
我不能使用server.route
,因为它会被上面显示的callback
捕获。
使文件仍可下载的最佳方法是什么?
好的,我现在已经解决了
在 documentation 中说:
The dcc.Location
component represents the location or address bar in your web browser.
所以我在下载选项中使用了 html.A 元素。如所述 here、download
Prompts the user to save the linked URL instead of navigating to it.
这意味着当用户点击 link 时,它不会更改地址栏。因此,display_page(pathname)
中的 callback
不会被调用,link 通过 @server.route
语句指向 download(path)
方法。
我想通了
注意目录stage是我存放临时下载文件的地方
首先,这是我的目录树的样子:
root
|
|---apps
| └── main.py
|---assets
|---layouts
| |--layout.py
| └── error.py
|---stage
|---app.py
|---functions.py
└── index.py
这就是我的 index.py 的样子
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from app import app
from apps import main
app.layout = html.Div(
[dcc.Location(id="url", refresh=False), html.Div(id="page-content")]
)
@app.callback(Output("page-content", "children"), [Input("url", "pathname")])
def display_page(pathname):
if pathname == "/main":
return main.layout
else:
return main.error_page
if __name__ == "__main__":
app.run_server(debug=True, host="llp-lnx-dt-10200", port=15021)
在functions.py我会动态生成一个破折号-table+html.A(...
和link来下载,函数是
def display_final_results(table):
import dash_html_components as html
import dash_core_components as dcc
import dash_table
import pandas as pd
return html.Div(
[
html.H5("""File processed and stuff worked"""),
dash_table.DataTable(
id="result_table",
data=table.iloc[:20, :].to_dict("records"),
columns=[{"name": i, "id": i} for i in list(table)],
),
html.Hr(),
dcc.Store(id="result_vault", data=table.to_dict()),
html.A(id="download_link", children=html.Button(children="Download")),
]
)
in main.py 我调用函数 def_final_results(table)
传递我想在破折号中显示的 table-table 以及 link 供下载。
这是 main.py 中的回调,然后是 app.server.route()
@app.callback(
Output("download_link", "href"),
[Input("result_vault","data"),
Input("h5_filename", "children")]
)
def return_download_link(data, upload_filename):
shutil.rmtree("stage")
os.mkdir("stage")
target = pd.DataFrame(data)
download_filename = upload_filename.split(":")[1].strip() + f"""_{filestamp()}.xlsx"""
uri = f"""stage/{download_filename}"""
target.to_excel(
uri, engine="xlsxwriter", index=False, header=True, sheet_name="results"
)
return uri
@app.server.route("/stage/<path:path>")
def serve_static(path):
root_dir = os.getcwd()
return flask.send_from_directory(os.path.join(root_dir, "stage"), filename=path)
在main.py中tabletarget
被保存到目录/stage
和uri
对象中这是文件的路径/stage/filename+filestamp
被发送到id为download_link的对象作为href
属性,这是文件[=中的html.A(...
63=]。我返回了 href
,因为 download
属性对我不起作用。
我犯的最大错误是我的index.pydcc.Location
url曾经是:
if pathname == "apps/main":
return main.layout
所以每次路由都会去https://llp-lnx-dt-10200:15021/apps/stage/filename
而不是 https://llp-lnx-dt-10200:15021/stage/filename
.
通过从 url 中删除应用程序,问题迅速得到解决。
我已经知道以下方法 (link here):
server = Flask(__name__)
app = dash.Dash(server=server)
@server.route("/download/<path:path>")
def download(path):
"""Serve a file from the upload directory."""
return send_from_directory(UPLOAD_DIRECTORY, path, as_attachment=True)
但问题是,当我使用 Plotly 建议的多页方法时(link here(低于 "Structuring a Multi-Page App" - index.py
)):
app.layout = html.Div([
dcc.Location(id='url', refresh=False),
html.Div(id='page-content')
])
@app.callback(Output('page-content', 'children'),
[Input('url', 'pathname')])
def display_page(pathname):
if pathname == '/apps/app1':
return app1.layout
elif pathname == '/apps/app2':
return app2.layout
else:
return '404'
我不能使用server.route
,因为它会被上面显示的callback
捕获。
使文件仍可下载的最佳方法是什么?
好的,我现在已经解决了
在 documentation 中说:
The
dcc.Location
component represents the location or address bar in your web browser.
所以我在下载选项中使用了 html.A 元素。如所述 here、download
Prompts the user to save the linked URL instead of navigating to it.
这意味着当用户点击 link 时,它不会更改地址栏。因此,display_page(pathname)
中的 callback
不会被调用,link 通过 @server.route
语句指向 download(path)
方法。
我想通了
注意目录stage是我存放临时下载文件的地方
首先,这是我的目录树的样子:
root
|
|---apps
| └── main.py
|---assets
|---layouts
| |--layout.py
| └── error.py
|---stage
|---app.py
|---functions.py
└── index.py
这就是我的 index.py 的样子
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from app import app
from apps import main
app.layout = html.Div(
[dcc.Location(id="url", refresh=False), html.Div(id="page-content")]
)
@app.callback(Output("page-content", "children"), [Input("url", "pathname")])
def display_page(pathname):
if pathname == "/main":
return main.layout
else:
return main.error_page
if __name__ == "__main__":
app.run_server(debug=True, host="llp-lnx-dt-10200", port=15021)
在functions.py我会动态生成一个破折号-table+html.A(...
和link来下载,函数是
def display_final_results(table):
import dash_html_components as html
import dash_core_components as dcc
import dash_table
import pandas as pd
return html.Div(
[
html.H5("""File processed and stuff worked"""),
dash_table.DataTable(
id="result_table",
data=table.iloc[:20, :].to_dict("records"),
columns=[{"name": i, "id": i} for i in list(table)],
),
html.Hr(),
dcc.Store(id="result_vault", data=table.to_dict()),
html.A(id="download_link", children=html.Button(children="Download")),
]
)
in main.py 我调用函数 def_final_results(table)
传递我想在破折号中显示的 table-table 以及 link 供下载。
这是 main.py 中的回调,然后是 app.server.route()
@app.callback(
Output("download_link", "href"),
[Input("result_vault","data"),
Input("h5_filename", "children")]
)
def return_download_link(data, upload_filename):
shutil.rmtree("stage")
os.mkdir("stage")
target = pd.DataFrame(data)
download_filename = upload_filename.split(":")[1].strip() + f"""_{filestamp()}.xlsx"""
uri = f"""stage/{download_filename}"""
target.to_excel(
uri, engine="xlsxwriter", index=False, header=True, sheet_name="results"
)
return uri
@app.server.route("/stage/<path:path>")
def serve_static(path):
root_dir = os.getcwd()
return flask.send_from_directory(os.path.join(root_dir, "stage"), filename=path)
在main.py中tabletarget
被保存到目录/stage
和uri
对象中这是文件的路径/stage/filename+filestamp
被发送到id为download_link的对象作为href
属性,这是文件[=中的html.A(...
63=]。我返回了 href
,因为 download
属性对我不起作用。
我犯的最大错误是我的index.pydcc.Location
url曾经是:
if pathname == "apps/main":
return main.layout
所以每次路由都会去https://llp-lnx-dt-10200:15021/apps/stage/filename
而不是 https://llp-lnx-dt-10200:15021/stage/filename
.
通过从 url 中删除应用程序,问题迅速得到解决。