【问题标题】:Downloading dynamically generated files from a Dash/Flask app从 Dash/Flask 应用程序下载动态生成的文件
【发布时间】: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 似乎不正确,因为单击按钮只会重定向到索引页面。需要什么才能让它发挥作用?

【问题讨论】:

    标签: python flask plotly-dash


    【解决方案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
    

    【讨论】:

    • 你能解释一下语法“”吗?为什么2,为什么:?
    【解决方案2】:

    解决办法:

    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 组件,用于基于用户的动态下载。它不需要创建自定义按钮,uuidflask.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)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-28
        • 1970-01-01
        相关资源
        最近更新 更多