Hi all,
I would like to create a zip file and download it by using dcc.download. Creating the zip works fine but I’ve no idea how to hand it over to the output of the dcc.downlaod. For the moment the zip is going to be safed at the same point in system where the executed file is stored.
Below is my code:
import dash
from dash.dependencies import Output, Input
import dash_html_components as html
import dash_core_components as dcc
import matplotlib.pyplot as plt
import zipfile
import io
app = dash.Dash(prevent_initial_callbacks=True)
app.layout = html.Div([html.Button("Download zip", id="btn_txt"),
dcc.Download(id="download-text")]
)
@app.callback(
Output("download-text", "data"),
Input("btn_txt", "n_clicks"),
prevent_initial_call=True,
)
def func(n_clicks):
zip_file_name = "my_export.zip"
with zipfile.ZipFile(zip_file_name, mode="w") as zf:
for i in range(3):
plt.plot([i, i])
buf = io.BytesIO()
plt.savefig(buf)
plt.close()
img_name = "fig_{:02d}.png".format(i)
zf.writestr(img_name, buf.getvalue())
if __name__ == "__main__":
app.run_server(debug=False)
Thanks for your answer.
But is there any way to create the zip just in the app without saving it local and drop it directly to dcc.download? Because I’m not shure, weather I’ve accesse to the lokal storage later.
Here is a small example of in-memory download of a PDF file using send_bytes. I would imagine that a similar approach would be feasible for a zip file,
Well, I tried to make it out of this thread you postet and I think I’m not far away to achive it. I modified the code that the zip file is created in a stream. But how can I download this stream using dcc.download?
import io
from os import close
import dash
from dash.dependencies import Output, Input
from dash_extensions.snippets import send_bytes
import dash_html_components as html
import dash_core_components as dcc
import matplotlib.pyplot as plt
import zipfile
from io import BytesIO
app = dash.Dash(prevent_initial_callbacks=True)
app.layout = html.Div([html.Button("Download zip", id="btn_txt"),
dcc.Download(id="download-text")]
)
@app.callback(
Output("download-text", "data"),
Input("btn_txt", "n_clicks"),
prevent_initial_call=True,
)
def func(n_clicks):
archive = BytesIO()
with zipfile.ZipFile(archive, mode="w") as zf:
for i in range(3):
plt.plot([i, i])
buf = io.BytesIO()
plt.savefig(buf)
plt.close()
img_name = "fig_{:02d}.png".format(i)
zf.writestr(img_name, buf.getvalue())
return send_bytes(archive, "some_name.zip") # does not work
if __name__ == "__main__":
app.run_server(debug=False)
Thanks HConBike and Emil, this worked for me as well. For those wanting to wrap multiple pandas dataframes as individual csv files inside a single zip file for download, you can convert dataframes to bytes using df.to_csv().encode(‘utf-8’) as per this answer on SO. The callback I used then looks like this:
@app.callback(Output("download-text", "data"), Input("btn_txt", "n_clicks"))
def func(n_clicks):
def write_archive(bytes_io):
with zipfile.ZipFile(bytes_io, mode="w") as zf:
for df,fn in zip(df_list,fileNameList):
df_bytes=df.to_csv(index=False).encode('utf-8')
fileName = f'{fn}.csv'
zf.writestr(fileName ,df_bytes)
return dcc.send_bytes(write_archive, "zipFileName.zip")