Failed to download zip folder in Dash app when deployed to RSConnect

I have a Dash application that I would like the user to download a zip folder from. The following reproducible Python code creates an app that downloads a zip folder with some dummy tables in csv format:

import dash
from dash.dependencies import Output, Input
import dash_html_components as html
import dash_core_components as dcc

import os
import zipfile
import tempfile

# helper function for closing temporary files
def close_tmp_file(tf):
    try:
        os.unlink(tf.name)
        tf.close()
    except:
        pass

# app layout
app = dash.Dash(prevent_initial_callbacks=True)
app.layout = html.Div(
    [
        html.Button("Download ZIP folder with dummy csv", id="btn_zip"),
        dcc.Download(id="download-dataframe-zip"),
    ]
)

# sample dataframes for zipping
import pandas as pd

df1 = pd.DataFrame({"a": [1, 2, 3, 4], "b": [2, 1, 5, 6], "c": ["x", "x", "y", "y"]})
df2 = pd.DataFrame({"d": [6, 2, 3, 9], "e": [3, 2, 7, 8], "f": ["t", "v", "s", "b"]})

df_dict = {"df1":df1,"df2":df2}

# app callbacks
@app.callback(
    Output("download-dataframe-zip", "data"),
    Input("btn_zip", "n_clicks"),
    prevent_initial_call=True,
)
def func(n_clicks):

    zip_dict = {}

    # add dataframes to zip file using temporary files
    for name,df in df_dict.items():
        df_temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.csv')
        df.to_csv(df_temp_file.name)
        df_temp_file.flush()
        zip_dict[name] = df_temp_file.name

    zip_tf = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
    zf = zipfile.ZipFile(zip_tf, mode='w', compression=zipfile.ZIP_DEFLATED)

    for name,fn in zip_dict.items():
        zf.write(fn,f"{name}.csv")
    
    # close uploaded temporary files
    zf.close()
    zip_tf.flush()
    zip_tf.seek(0)

    [close_tmp_file(_tf) for _tf in zip_dict]
    close_tmp_file(zip_tf) # the app works if I remove this

    return dcc.send_file(zip_tf.name,filename="mydfs.zip")


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

The app works locally, but once its deployed I get the following error message :

FileNotFoundError: [Errno 2] No such file or directory: '/opt/rstudio-connect/mnt/tmp/tmp76hltf2h.zip'

It seems like it's trying to access the temporary file but it may have already been deleted before its returns .

The problem is solved if I don't close the temporary file (remove line close_tmp_file(zip_tf) prior to the return dcc.send_file(zip_tf.name,filename="mydfs.zip") command

My concern is that this temporary stays in the RSConnect server and won't be deleted/closed. Or perhaps it will when the app is closed by the user?

Thanks for the help in advanced!

Hi @squiroga !! Welcome to RStudio Community! Whew! Where does time go!?

Thanks so much for reaching out here! This is an awesome example / question!

I suspect that you have pinned the root cause here - closing the temporary file will allow python to delete it (and python seems to do that extremely quickly). This is actually a problem that I have encountered before (in a very different context)

In any case, I think not closing the temp file is the right bet, and that you should rest at ease knowing that the linux operating system often has mechanisms for cleaning up /tmp space. Moreover, I believe Connect also has some /tmp space cleanup for the processes that it maintains (not everyone is as conscientious as you and cleans up after themselves :wink: ).

If you see otherwise, I would be very curious to hear more, though! And hopefully next time, we can respond in a bit more timely fashion :grinning_face_with_smiling_eyes: