DEV Community

Shreyaan Seth
Shreyaan Seth

Posted on

Unlocking Smooth Downloads: A Guide to User-Friendly File Download After User Actions (POST Requests)

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>
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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");
Enter fullscreen mode Exit fullscreen mode

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)