Problems downloading a binary dynamically generated file


I have a dynamically generated file created on backend that I want to download when clicking a button. File is verified as being well generated and downloadable by other means.

On Dash I have the following for the button:

        Generate pdf file",

and the callback:

    Output("download-pdf", "data"),
    Input(f"{PAGE_ID}_export_button", "n_clicks"),
    [State(f"{PAGE_ID}_labelsperserial", "value"),
    State(f"{PAGE_ID}_offsetlines", "value"),
    State(f"{PAGE_ID}_filename", "value"),
    State(f"{PAGE_ID}_start", "value"),
    State(f"{PAGE_ID}_devicetype", "value"),
    State(f"{PAGE_ID}_batchname", "value"),
    State(f"{PAGE_ID}_batchsize", "value"),
    State(f"{PAGE_ID}_revision", "value")],
def download_pdf(n_clicks, labelsperserial, offsetlines, filename, start, devicetype, batchname, batchsize, revision):
    fn = filename
    if fn is not None and not fn.endswith(".pdf"):
        fn = fn + ".pdf"
    response = requests.get(
        base_url + f"/labels/generate-microscopes-serial-sheet?labelsperserial={labelsperserial}&offsetlines={offsetlines}" +
    return dict(content=response.content, filename=fn)

And I’m getting the following error message for it:

Traceback (most recent call last):
  File "C:\Users\Lucas\anaconda3\envs\dashApp2\lib\site-packages\dash\", line 191, in add_context
    jsonResponse = to_json(response)
  File "C:\Users\Lucas\anaconda3\envs\dashApp2\lib\site-packages\dash\", line 21, in to_json
    return to_json_plotly(value)
  File "C:\Users\Lucas\anaconda3\envs\dashApp2\lib\site-packages\plotly\io\", line 124, in to_json_plotly
    return json.dumps(plotly_object, cls=PlotlyJSONEncoder, **opts)
  File "C:\Users\Lucas\anaconda3\envs\dashApp2\lib\json\", line 238, in dumps
  File "C:\Users\Lucas\anaconda3\envs\dashApp2\lib\site-packages\_plotly_utils\", line 59, in encode
    encoded_o = super(PlotlyJSONEncoder, self).encode(o)
  File "C:\Users\Lucas\anaconda3\envs\dashApp2\lib\json\", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "C:\Users\Lucas\anaconda3\envs\dashApp2\lib\json\", line 257, in iterencode
    return _iterencode(o, 0)
  File "C:\Users\Lucas\anaconda3\envs\dashApp2\lib\site-packages\_plotly_utils\", line 136, in default
    return _json.JSONEncoder.default(self, obj)
  File "C:\Users\Lucas\anaconda3\envs\dashApp2\lib\json\", line 180, in default
TypeError: Object of type 'bytes' is not JSON serializable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\Lucas\anaconda3\envs\dashApp2\lib\site-packages\flask\", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\Users\Lucas\anaconda3\envs\dashApp2\lib\site-packages\flask\", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "C:\Users\Lucas\anaconda3\envs\dashApp2\lib\site-packages\dash\", line 1336, in dispatch
    response.set_data(func(*args, outputs_list=outputs_list))
  File "C:\Users\Lucas\anaconda3\envs\dashApp2\lib\site-packages\dash\", line 193, in add_context
    _validate.fail_callback_output(output_value, output)
  File "C:\Users\Lucas\anaconda3\envs\dashApp2\lib\site-packages\dash\", line 310, in fail_callback_output
    property=output.component_property, id=output.component_id
TypeError: unsupported format string passed to dict.__format__

I already tried to use return send_bytes(response.content, fn) instead of return dict(content=response.content, filename=fn) in order to download the file and I got the following error :

Traceback (most recent call last):
  File "C:\Users\Lucas\Projects\ovizio-dashboard\snackyApp\snacky_lib\snacky\apps\generate_labels\", line 179, in download_pdf
    return send_bytes(response.content, fn)
  File "C:\Users\Lucas\anaconda3\envs\dashApp2\lib\site-packages\dash_extensions\", line 43, in send_bytes
    writer(data_io, **kwargs)
TypeError: 'bytes' object is not callable

I’m pretty stuck. Could anybody help me on that?
Thanks in advance!

You missed this dcc.send_file or this,

base64 (boolean; optional): Set to True, when data is base64 encoded.

And you may like this below.

1 Like

Encoding to base64 didn’t work and, dcc.send_file is not an option because the file is remote and I’m getting the stream, unless I save the stream in a file and send it again, which is counterproductive. But I’ll think this as a last possibility.

Sorry, I haven’t used requests in a while, how about io.BytesIO module?

Did you try send_bytes? :slight_smile:

I already tried and wrote it in the question. The error I get is TypeError: 'bytes' object is not callable.

The send_bytes function expects a function that writes to BytesIO (as shown in the SO example), not a byte array.

Well, what happened is very strange.
Even using prevent_initial_call=True) on callback decorator, the function is being called on page load, and this call was breaking things inside. I couldn’t investigate more, and I think it could worth a bug report.

What I did is to add following lines at the beginning of the function:

    changed_id = [p['prop_id'] for p in callback_context.triggered][0]
    if changed_id != f"{PAGE_ID}_export_button.n_clicks":
        return no_update

    if n_clicks is None:
        return no_update

And then the rest of the function. Then dcc.send_bytes began working, because before it was failing silently.

Are some of the input/output components generated dynamically?