What Happens When You Click "Download"
First, let's understand what occurs when you click a "Get File" or "Download File" button on a browser. It turns out it's simply a basic GET
API call. We call the URL using the href
attribute on an <a>
tag, and it returns a file, which we can consider a blob. By adding the download
attribute to the <a>
tag, we can simply download that blob.
<a href="path/to/your/file.pdf" download="myfile.pdf">Download File</a>
The browser then intercepts the download and shows progress, speed, and the estimated time remaining (ETA), creating a great user experience (UX). The user is updated and doesn't get confused at any point.
Here's an example:
The Issue with Uploading Data
Now, sometimes we need to send some data to the server to manipulate a file, usually to create a new one. Here's where things get tricky. To do this, you need to make a POST
request. The server receives the request, performs the necessary tasks, and then sends the file. So far, so good.
The Problem with Downloading After POST
To download the file after a POST
request, we typically do something like this:
const response = await fetch(
url,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "John Doe" }),
}
);
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "file.zip";
link.target = "_blank";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
There's a problem with this approach, though. The browser's download manager doesn't intercept the download. Users have no idea when the file is ready, when the download started, what the speed is, or the estimated time remaining (ETA). This might be okay for tiny files, but it's a terrible experience for files larger than even 5 MB.
Here's an example of this issue:
The Fix: Storing and Downloading Later
So, how can we generate files on demand but still allow the browser to handle the download, displaying progress, ETA, and maybe even enabling parallel downloading?
The fix is simple: you have to store the file somewhere and send the link to the user. Then, just open it in a new tab.
const response = await fetch(
"url",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "John Doe" }),
}
);
const url = await response.text();
window.open(url, "_blank");
This way, the file is handled by the wonderful browser!
Here's an example of this solution:
Try it Yourself!
The examples I used are hosted here: https://post-request-blog.vercel.app/ You can try them out and fully understand what I mean. Feel free to snoop around in the network tab for the POST
requests to get a better grasp of the process.
Code Repository
Here's the GitHub repository for the code: https://github.com/Shreyaan/post-request-blog
Simplifications
For simplicity, I took some shortcuts, such as not using a proper backend with Node.js or Python and instead using Next.js. In a real-world scenario, a public folder on the backend would be used to store the file. Additionally, I'm using the first API's URL as the URL returned by the third API. Ideally, the third API would return the URL of the generated and stored file in the public folder.
I hope you enjoyed this and learned something new!
Top comments (0)