【问题标题】:How to download a file with plotly-dash on a multi-page app?如何在多页应用程序上使用 plotly-dash 下载文件?
【发布时间】:2020-04-09 15:24:42
【问题描述】:

我已经知道以下方法(链接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 建议的多页面方法时(链接 here(在“构建多页面应用程序”下方 - 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 捕获。

使文件仍可下载的最佳方法是什么?

【问题讨论】:

    标签: python flask plotly-dash


    【解决方案1】:

    好的,我已经解决了。

    documentation 中写道:

    dcc.Location 组件表示您的网络浏览器中的位置或地址栏。

    所以我使用了带有下载选项的 html.A 元素。如heredownload所述

    提示用户保存链接的 URL,而不是导航到它。

    这意味着当用户点击链接时,它不会改变地址栏。因此,不会调用来自display_page(pathname)callback,而是通过@server.route 语句将链接定向到download(path) 方法。

    【讨论】:

    • 你能分享你的解决方案吗?
    • 你能分享你的代码吗?你分享了面包屑。如果我弄清楚了,我会在下面发布解决方案。
    • 嗨,我不在公司了,因此没有代码了。但我想我用它就像html.A(download='filename.txt')
    • 感谢您的回复,还有一个问题,您还记得您的/download 目录在哪里吗?是在apps 中还是在您的根目录中。无论我尝试做什么,@server.route 总是重定向到应用程序/下载。如果我把download 放在apps/ 里面,那么它就会跑到apps/apps/download。这就像在多页应用中玩猫捉老鼠一样。
    【解决方案2】:

    我想通了
    请注意,目录阶段是存储我的临时下载文件的位置
    首先这是我的目录树的样子:

       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中我会动态生成一个dash-table + html.A(...和下载链接,函数是

    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")),
            ]
        )
    

    ma​​in.py中,我调用了函数def_final_results(table),传入了我想在dash-table中显示的表格以及下载链接。

    ma​​in.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)
    

    ma​​in.py中,表target被保存到目录/stage中,uri对象是文件/stage/filename+filestamp的路径被发送到ID为download_link的对象作为href 属性,这是文件functions.py 中的html.A(...。我返回了href,因为download 属性对我不起作用。

    我犯的最大错误是我的 index.py dcc.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 中删除应用程序,问题很快得到解决。

    【讨论】:

      猜你喜欢
      • 2021-11-09
      • 2019-05-16
      • 1970-01-01
      • 2021-02-08
      • 2020-04-22
      • 2022-01-20
      • 1970-01-01
      • 1970-01-01
      • 2020-09-04
      相关资源
      最近更新 更多