Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return JSON and a File

Tags:

python

flask

How can I return a JSON response and a file response:

Right now I do this:

runNumber = "A0001"
response = None
try:
    response = make_response("Line One\r\nLine Two\r\n")
    response.headers["Content-Disposition"] = "attachment; filename=" + runNumber + ".txt"
except MyCustomException as e:
    response = jsonify(error=e.value, runnumber=runNumber)
except:
    raise
return(response)

But that only allows me to return JSON OR a File. In some cases, I want to return both.

[edit:] The case where I want to return JSON and a file is when there is a warning about the file contents that the user should check before using the file.

If this is not possible, I will add the warning to the contents of the file.

like image 887
Be Kind To New Users Avatar asked May 30 '15 15:05

Be Kind To New Users


People also ask

How do I return a JSON file?

stringify() to return JSON data): We will now use http. createServer() and JSON. stringify() to return JSON data from our server.

How do I return a JSON response?

To return JSON from the server, you must include the JSON data in the body of the HTTP response message and provide a "Content-Type: application/json" response header. The Content-Type response header allows the client to interpret the data in the response body correctly.

How do I save JSON data to a file?

Another way of writing JSON to a file is by using json. dump() method The JSON package has the “dump” function which directly writes the dictionary to a file in the form of JSON, without needing to convert it into an actual JSON object.

What does JSON () return Python?

json() returns a JSON object of the result (if the result was written in JSON format, if not it raises an error). Python requests are generally used to fetch the content from a particular resource URI.


2 Answers

You cannot just return two responses. You get to return just the one.

That means that if you really need to return both JSON and a file you need to come up with a scheme that lets you return the two in one response and let the client separate out the file and JSON parts again.

There is no standard for this. Whatever you come up with will need to be carefully documented for your clients to handle explicitly.

You could use a custom header to store the JSON data, for example:

response = make_response("Line One\r\nLine Two\r\n")
response.headers["Content-Disposition"] = "attachment; filename=" + runNumber + ".txt"
response.headers['X-Extra-Info-JSON'] = json.dumps(some_object)

Or you could put the file contents in the JSON data. JSON isn't the greatest format for binary data, you may want to encode the binary data to Base64 first:

filedata = "Line One\r\nLine Two\r\n".encode('base64')
return jsonify(name=runNumber + '.txt', data=filedata)

Or you could create a multipart MIME document, in the same way that a POST multipart/form-data body works.

What you pick depends on your use-cases (what kind of clients are using your API) and the size of the data (megabytes of file data in a JSON response is not very workable).

like image 128
Martijn Pieters Avatar answered Oct 03 '22 06:10

Martijn Pieters


I had a similar issue with sending both an audio file and its transcript. Unfortunately, we cannot send two responses for a single API request. Also, making two API calls for a single task did not make sense. My workaround was to pass the binary data of the file encoded as a string through JSON and decode it in the frontend. I used python and flask in backend and react-js in frontend. Here is a snippet of the code. You have to modify the decoding section based on the file you are sending.

Python:

import base64
response = {
    "audioContent": base64.b64encode(binaryData).decode(),
    "botText": botText
}
return json.dumps(response)

JS:

fetch(URL, requestData).then((response) => response.json()).then((data) => {
    var binary_string = window.atob(data.audioContent);
    var len = binary_string.length;
    var bytes = new Uint8Array(len);
    for (var i = 0; i < len; i++) {
      bytes[i] = binary_string.charCodeAt(i);
    }
    // console.log("Binary Data: ", binaryData);
    var url = window.URL.createObjectURL(
      new Blob([bytes.buffer], { type: "audio/mp3" })
    );
    console.log("URL: ", url);
    var a = new Audio(url);
    a.play();
}

There are two points to remember:

  1. This works best when the size of the file being sent is small. Large files will generally affect your API responses
  2. The decoding of the file in frontend is quite slow in my case. I did not get much time to improve performance, but it did the task for me then.
like image 41
crazy_lazy_life Avatar answered Oct 03 '22 06:10

crazy_lazy_life