'How do I download a file returned from FastAPI backend using Fetch API at the frontend?
This is my FastAPI(python) code, which returns a .ics
file:
@app.get("/latLong/")
async def read_item(lat: float,long:float):
mainFunc(lat,long)
return FileResponse("/tmp/myics.ics")
This is my frontend code in Javascript using Fetch API:
<script>
async function apiCall(long,lat) {
let myObject = await fetch('myapi.com/lat/long');
let myText = await myObject.text();
}
</script>
So from my visor (my api logs), it successfully calls the API. But from the front end, I am trying to get it to return the file.
The end result I would like to achieve is when the user clicks a button, the browser grabs the location, then sends the location to the API, and the API returns a file that the user can download.
Solution 1:[1]
First of, you need to adjust your endpoint on server side to accept path
parameters, as in the way it is currently defined, lat
and long
are expected to be query
parameters; however, in your javascript you are trying to send those as path
parameters. Thus, your endpoint should look like this:
@app.get("/{lat}/{long}/")
async def read_item(lat: float, long: float):
Next, set the filename
in FileResponse
, so that it can be included in the response Content-Disposition
header, which can later be retrieved on client side.
return FileResponse("/tmp/myics.ics", filename="myics.ics")
If you are doing a cross-domain request (also see FastAPI CORS), make sure to add Access-Control-Expose-Headers:Content-Disposition
to the response headers on the server (to expose the Content-Disposition
header), otherwise the filename
won't be accessible on client side.
headers = {'Access-Control-Expose-Headers': 'Content-Disposition'}
return FileResponse("/tmp/myics.ics", filename="myics.ics", headers=headers)
On client side, you could use a similar approach to this answer (the suggested downloadjs library by that answer is now outdated; thus, I wouldn't recommend using it). The below example also takes into account scenarios where the filename
includes unicode characters (i.e., -, !, (, )
, etc.) and hence, comes (utf-8 encoded) in the form of, for instance, filename*=utf-8''Na%C3%AFve%20file.txt
(see here for more details). In such cases, the decodeURIComponent()
function is used to decode the filename
. Working example below:
const url ='http://127.0.0.1:8000/41.64007/-47.285156'
fetch(url)
.then(res => {
const disposition = res.headers.get('Content-Disposition');
filename = disposition.split(/;(.+)/)[1].split(/=(.+)/)[1];
if (filename.toLowerCase().startsWith("utf-8''"))
filename = decodeURIComponent(filename.replace("utf-8''", ''));
else
filename = filename.replace(/['"]/g, '');
return res.blob();
})
.then(blob => {
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a); // append the element to the dom, otherwise it won't work in Firefox
a.click();
a.remove(); // afterwards, remove the element
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |