2

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 ?

Robin
  • 36,233
  • 5
  • 47
  • 99
  • I am not a react developer, but have you tried filesaver library? It worked very fine with us even for 5 Gigabytes file https://www.npmjs.com/package/file-saver – Samy Sammour Jun 21 '21 at 07:02
  • The general advice I've seen for this sort of operation is to generate a short-lived, hard-to-guess public URL on the server side and respond with that. You then set that URL into an `` element and click it – Phil Jun 21 '21 at 07:18
  • @Phil but this doesn't fix his problem, I guess! I think a file response with the filesaver package can solve the problem. Besides, sending a token will not be a problem since it is a normal observable/promise – Samy Sammour Jun 21 '21 at 07:25
  • 1
    @SamySammour I really don't think `file-saver` is a good fit for this just from reading their documentation... _"FileSaver.js is the solution to saving files on the client-side, and is perfect for web apps that **generates files on the client**. However if the file is coming from the server we **recommend you to first try to use `Content-Disposition` attachment response header** as it has more cross-browser compatibility."_ – Phil Jun 21 '21 at 07:28
  • @Phil aha, then I miss understood the question. I will take this into consideration when we have the functionality again. Thanks! – Samy Sammour Jun 22 '21 at 06:33

0 Answers0