0

I'm trying to download a dynamically generated json file with the html.A element in Dash using the following code:

download_json_link = html.A(
    'Download JSON',
    id='download-json',
    download='download.json',
    href='',
    target="_blank",
)

dir_path = os.path.dirname(__file__)
filepath_json = "/downloads/barcode.json"
filename_json = dir_path + filepath_json
f = open(filename_json, "a")
f.write(str_json)
f.close()

@app.server.route('/downloads/<path:path>')
def serve_static(path):
    root_dir = os.getcwd()
    return flask.send_from_directory(
        os.path.join(root_dir, 'downloads'), path
    )

It correctly generates and saves the json file to the downloads folder. However, when I press the html.A element, the downloaded file is a bunch of html like this, instead of the generated json file:

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Quantum Design</title>
        <link rel="icon" type="image/x-icon" href="/_favicon.ico?v=1.18.1">
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<link rel="stylesheet" href="https://codepen.io/chriddyp/pen/bWLwgP.css#">
    </head>
    <body>
        
<div id="react-entry-point">
    <div class="_dash-loading">
        Loading...
    </div>
</div>

        <footer>
            <script id="_dash-config" type="application/json">{"url_base_pathname": null, "requests_pathname_prefix": "/", "ui": true, "props_check": true, "show_undo_redo": false, "suppress_callback_exceptions": true, "update_title": "Updating...", "hot_reload": {"interval": 3000, "max_retry": 8}}</script>
            <script src="/_dash-component-suites/dash_renderer/polyfill@7.v1_8_3m1604000066.8.7.min.js"></script>
<script src="/_dash-component-suites/dash_renderer/react@16.v1_8_3m1604000066.14.0.js"></script>
<script src="/_dash-component-suites/dash_renderer/react-dom@16.v1_8_3m1604000066.14.0.js"></script>
<script src="/_dash-component-suites/dash_renderer/prop-types@15.v1_8_3m1604000066.7.2.js"></script>
<script src="/_dash-component-suites/dash_table/bundle.v4_11_1m1607375125.js"></script>
<script src="/_dash-component-suites/dash_html_components/dash_html_components.v1_1_1m1599150811.min.js"></script>
<script src="/_dash-component-suites/dash_core_components/dash_core_components.v1_14_1m1607542327.min.js"></script>
<script src="/_dash-component-suites/dash_core_components/dash_core_components-shared.v1_14_1m1607534701.js"></script>
<script src="/_dash-component-suites/dash_bootstrap_components/_components/dash_bootstrap_components.v0_12_0m1619764337.min.js"></script>
<script src="/_dash-component-suites/dash_renderer/dash_renderer.v1_8_3m1604000088.dev.js"></script>
            <script id="_dash-renderer" type="application/javascript">var renderer = new DashRenderer();</script>
        </footer>
    </body>
</html>

does anyone know what the issue is?

5eb
  • 14,798
  • 5
  • 21
  • 65
mjpablo23
  • 681
  • 1
  • 7
  • 23
  • Have you considered using the Download component instead? – emher Jul 21 '21 at 06:22
  • I've changed python3 tag to python since your question doesn't seem to be about a version specific issue. Feel free to correct if I'm wrong. – 5eb Jul 21 '21 at 10:00

2 Answers2

0

You could specify that the type of the data needs to be json and pass in a serialized version of your json data like this:

from dash import Dash
import dash_html_components as html
import os
import json

dir_path = os.path.dirname(__file__)
filepath_json = "/downloads/barcode.json"
filename_json = dir_path + filepath_json

with open(filename_json, encoding="utf-8") as f:
    data = json.load(f)
    data_string = json.dumps(data)

app = Dash(__name__)
app.layout = html.Div(
    html.A(
        "Download Data",
        id="download-link",
        download="download.json",
        href=f"data:text/json;charset=utf-8,{data_string}",
        target="_blank",
    )
)

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

https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs

https://napuzba.com/data-url/

5eb
  • 14,798
  • 5
  • 21
  • 65
  • ok thanks. how would I do this for .png and .bin files? – mjpablo23 Jul 22 '21 at 06:05
  • Let's keep this post focused on the original question and not add on additional questions to the same post. But basically for reading binary files you can look at [this](https://stackoverflow.com/q/1035340/9098350). You could convert that to base64 and use the approach in this answer but with a different data type. Or you could use the download component as emher suggested https://dash.plotly.com/dash-core-components/download. – 5eb Jul 22 '21 at 07:26
  • I tried to use dcc.download, but I get this error when I call dcc.send_file: AttributeError: module 'dash_core_components' has no attribute 'send_file'. I had to import download using dash extensions instead of dash core components. do I need to upgrade dash? – mjpablo23 Jul 27 '21 at 21:37
  • dash extensions shouldn't be necessary. You should probably update dash core components. Tested a sample out from the documentation and it works fine. – 5eb Jul 28 '21 at 07:15
0

it looks like when I update the href, I needed to use a relative path like this: "/downloads/barcode.json", instead of an absolute path.

mjpablo23
  • 681
  • 1
  • 7
  • 23