In my React app, I want to have a button to download a file that behaves identical to a <a href="..." download />
link:
- When clicking on it, the user gets asked immediately whether to download or open the file (before the download starts)
- The download progress is shown in the browser download list
- The download can be streamed to disk immediately, without filling up the browser memory so it works for large files as well
- I need to pass in a
Bearer
token in the header (which is the reason I cannot use the<a href download/>
approach)
The solution I find online is using
fetch(... including the bearer token in the header)
.then(res => res.blob() )
.then(data => {
const link = document.createElement("a");
link.href = window.URL.createObjectURL(new Blob([data], { type: "application/octet-stream" }));
link.download = fileName;
document.body.append(link);
link.click();
window.URL.revokeObjectURL(link.href);
});
but this has 2 obvious drawbacks:
- The user only gets to see the "download or open" dialog after the file has downloaded. For large files where the download takes a while, it simply looks like nothing is happening after clicking
- For downloads that don't fit into the memory of the browser, it crashes.
I also saw the downloads.download()
API but that doesn't work in Safari yet so I didn't try it as I need to support all major browsers.
The last option (that I didn't explore in detail) is using the fetch().then(res => res.body)
and do something with the stream. It would allow me to stream the download directly to a file, but I fail to see how I would ever show the "download or open" dialog to the user, and how to get access to the file location the user chooses for the download.
Having a button to download a (large) file in a React app doesn't sound like a far fetched requirement, so I'm probably overlooking something trivial. Can somebody point me in the right direction ?