Dcc.send_bytes/download on clientside

Hey,
here is an example of downloading a .zip-file using clientside callbacks. Might be useful for some :man_shrugging:

from dash import Dash, html, dcc, Input, Output, no_update
import io
import zipfile
import json

app = Dash(__name__)

app.layout = html.Div([
    html.Button('Download Clientside', id='button1'),
    html.Button('Download normal', id = 'button2'),
    dcc.Download(id = 'download')
])

app.clientside_callback(
'''
function (n){
    var zip = new JSZip();

    zip.file('mydata.json', JSON.stringify(["mydata2"]));

    return zip.generateAsync({ type: "base64" })
        .then(function (base64String) {
            return {'content': base64String, 'filename': 'data.zip', 'type': null, 'base64': true};
        })
        .catch(function (error) {
            console.error("Error generating zip: ", error);
            return dash_clientside.no_update;
        });

}
''',
    Output('download', 'data'),
    Input('button1', 'n_clicks'),
    prevent_initial_call =True
)

@app.callback(
    Output('download', 'data', allow_duplicate=True),
    Input('button2', 'n_clicks'),
    prevent_initial_call =True
)
def send_download(n):
    if not n:
        return no_update
    bytes_io = io.BytesIO()
        
    with zipfile.ZipFile(bytes_io, 'w') as zipf:
            zipf.writestr('mydata.json', json.dumps(["mydata"]))
    sended_bytes  = dcc.send_bytes(bytes_io.getvalue(), "data.zip")
    return sended_bytes


if __name__ == '__main__':
    app.run(debug=True)

you also need to include jszip.min.js in your assets folder.

5 Likes

Cool and useful example. Thanks for sharing, @Louis :clap:

1 Like