dcc.Download from a public S3 bucket

I would like to use the download component to point at a file that is in an AWS S3 bucket. Below is essentially what I am attempting but am getting:

OSError: [Errno 22] Invalid argument: 'https://bucketname.s3.us-east-2.amazonaws.com/download/123456.csv'

Is it possible to do this? I have changed the actual bucket name in both the code and error… thank you

from dash import Dash, dcc, html, Input, Output

app = Dash(__name__)
app.layout = html.Div([
    html.Button("Download Image", id="btn_image"),
    dcc.Download(id="download-image")
])

@app.callback(
    Output("download-image", "data"),
    Input("btn_image", "n_clicks"),
    prevent_initial_call=True,
)
def func(n_clicks):
    return dcc.send_file(
       'https://bucketname.s3.us-east-2.amazonaws.com/download/123456.csv'
    )


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

Hi,

I believe that dcc.send_file expects the path to be local, not in http… If you are using this callback with the sole purpose of download the file, you could consider replacing the button by a html.A component with href pointing at the URL in the public bucket. That’s probably the easiest approach to download a file like this.

Thank you. The final use for this component will not have a button - it will automatically download. I just grabbed this script from the Dash site as an example of what I am trying to achieve.

I know I can make this work by reading the URL into a dataframe and then converting this dataframe into a csv as a download, but this requires unnecessary steps that I was hoping could be avoided.

Any thoughts on if this is possible?

Sorry, I don’t understand what you mean. Are you asking if there is a way to download the csv straight from the url?

When the file is already online, you don’t need the Download component. Its intended usage is for data located on the server.

Thank you. The file is already online. My desire is that the output of the callback returns the file that is online to the users local machine via an automatic download. I apologize if I was clear enough earlier.

No worries. As I said on top, you can accomplish that by using a html.A tag, which will just generate a link. If you really want the visual of a button, then you can wrap the a tag in a button and remove the decoration with custom css (like this). No need for a callback.

Thanks. I am not after a button - I am after making it download automatically (the download file that pops up on the bottom of the screen and shows the progress).

You can use a construct where like this,

from dash_extensions.enrich import DashProxy, dcc, html, Output, Input, Trigger, NoOutputTransform, TriggerTransform

app = DashProxy(transforms=[NoOutputTransform(), TriggerTransform()], prevent_initial_callbacks=True)
app.layout = html.Div([
    html.Button("Download", id="btn_download"), dcc.Store(id="url_to_open")
])


@app.callback(Output("url_to_open", "data"), Trigger("btn_download", "n_clicks"))
def func():
    return 'https://filesamples.com/samples/document/csv/sample4.csv'


# Js snipper that opens the url (i.e. invokes download) in a new window.
js_download = """
function download(dataurl) {
  const link = document.createElement("a");
  link.href = dataurl;
  link.click();
}
"""
# Bind client side callback with dummy output, as we just need to run the js snippet.
app.clientside_callback(js_download, Input("url_to_open", "data"))

if __name__ == "__main__":
    app.run_server(debug=True, port=7777)

where you create a temporary link and click it to start the download automatically. I guess this is actually a pretty common use case, so it might have made sense to include it in the Download component.

3 Likes