Muhi Logo Text
AboutBlogWork With Me

How to Save and Download Files in JavaScript

Learn how to create a custom function to download one or multiple files in JavaScript

Last updated on December 12, 2021

javascript
Save As

In the web world, “save file”, “save as”, or “download file” are used interchangeably and mean the same thing, which is to download and save a file to the client’s machine. There isn’t a built-in method in JavaScript that allows us to do so, but there are a few ways to achieve this. In this tutorial, we’ll explore different ways to save files in JavaScript programmatically, including how to download multiple files in one hit.

Download and save a file using an anchor element

The easiest way to download and save a file programmatically in JavaScript is to dynamically create an anchor element with a download attribute and invoke the click event. Let’s create a function called saveFile that takes a URL and a file name as parameters and does the following:

  1. Create an anchor element
  2. Set the href to the URL
  3. Set the download attribute to the file name
  4. Append the anchor to the body
  5. Invoke the click event
  6. Remove the anchor from the body
function saveFile(url, filename) {
  const a = document.createElement("a");
  a.href = url;
  a.download = filename || "file-name";
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
}

Although appending the anchor to the body is not always necessary, it’s a good practice to do so to avoid any issues with the click event on some browsers with different configurations.

We can consume the function as follows:

saveFile("/files/myFile.txt", "myFile.txt");

We are not limited to static files. We can also download files that are generated dynamically. For example, we can create a Blob object and convert it to a URL.

const file = new Blob(["Hello, file!"], { type: "text/plain" });
const url = window.URL.createObjectURL(file);
saveFile(url, "myFile.txt");
window.URL.revokeObjectURL(url);

createObjectURL creates a URL for the Blob object, and it’s only valid for the duration of the page session unless we call revokeObjectURL to release the object.

Download and save a file using the fetch API

Another way to download and save a file is to use the fetch API to get the file content first and then use the Blob object to create a URL. This approach has has a couple of advantages:

  • We can get the file content from a different domain, especially with cloud storage like AWS S3 or Azure Blob Storage. If we try to download a file from a different domain using the anchor element, it will redirect us to the file instead of downloading it.
  • fetch is designed to work with the Streams API, allowing it to process large amounts of data in chunks without loading the entire file into memory. This is particularly useful for downloading large files or working in environments with limited memory.

Let’s create a function called downloadFile that takes a URL and a file name as parameters and does the following:

  1. Fetch the file content
  2. Convert the response to a blob
  3. Create a URL from the blob
  4. Use the saveFile function we did earlier to download the file
async function downloadFile(url, filename) {
  try {
    const response = await fetch(url, {
      headers: {
        Accept:
          "application/json, text/plain,application/zip, image/png, image/jpeg, image/*",
      },
    });
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const blob = await response.blob();
    const blobUrl = URL.createObjectURL(blob);
    saveFile(blobUrl, filename);

    URL.revokeObjectURL(blobUrl);
  } catch (err) {
    console.error("Error in fetching and downloading file:", err);
  }
}

Notice that we are passing the Accept header to the fetch function to specify the file type. This is important because the fetch function will try to guess the file type based on the response headers, and if it fails, it will throw an error.

Now, we can consume the function with different file types and cross-domain URLs:

downloadFile("https://httpbin.org/robots.txt", "textFile.txt");
downloadFile("https://httpbin.org/image", "imageFile.jpg");
downloadFile("https://httpbin.org/json", "jsonFile.json");

Use file-saver library

FileSaver.js is an excellent solution to saving files on the client-side without having to write our custom code.

First, we need to install the library:

npm i file-saver

Then, use it as follows:

const FileSaver = require("file-saver");

// Save Blob file
const file = createBlob("Hello, file!");
FileSaver.saveAs(file, "myFile.txt");

// Save URL
FileSaver.saveAs("https://httpbin.org/image", "image.jpg");

The saveAs function takes two parameters:

  • The file content, which can be a Blob object or a URL
  • The file name

We’re all done! FileSaver will take care of the rest of the logic for us.

Zip and download multiple files

In some instances, we need to download multiple files in one hit. Although this can be achieved by looping through the files and triggering the save function, this will end up with many files added to the download folder.

An alternative solution is to use JSZip, a library for creating, reading, and editing .zip files.

First, we need to install the library:

npm i jszip

Then, in the following code snippet, we’ll create a zip file with two text files and download it:

const zip = new JSZip();
const file1 = new Blob(["Hello, file 1!"], { type: "text/plain" });
const file2 = new Blob(["Hello, file 2!"], { type: "text/plain" });
zip.file("file1.txt", file1);
zip.file("file2.txt", file2);
const content = await zip.generateAsync({ type: "blob" });
const contentUrl = URL.createObjectURL(content);
saveFile(contentUrl, "zipContent.zip");

Additionally, we can use the FileSaver’s saveAs function instead of the saveFile function we created earlier.

Browser SaveAs configuration

Typically, when the file is downloaded, it will be saved in the download folder. However, the user can configure the browser to “saveAs” and choose a different location to save the file.

In Chrome, the user can go to Settings > Advanced > Downloads and toggle the “Ask where to save each file before downloading” option.

In Edge, the user can go to Settings > Downloads and toggle the “Ask me what to do with each download” option.

In Firefox, the user can go to Options > General > Files and Applications and toggle the “Always ask you where to save files” option.

In Safari, the user can go to Preferences > General and toggle the “File download location” option.

Demo

Conclusion

Creating a custom solution to download and save files programmatically in JavaScript gives us flexibility and control but can be time-consuming and error-prone. Using a library like FileSaver.js saves us time and effort as the library is well-tested and maintained.

Bye for now 👋

If you enjoyed this post, I regularly share similar content on Twitter. Follow me @muhimasri to stay up to date, or feel free to message me on Twitter if you have any questions or comments. I'm always open to discussing the topics I write about!

Recommended Reading

Learn how to replace multiple words and characters using regular expressions and replaceAll function in JavaScript

javascript

Discussion

Upskill Your Frontend Development Techniques 🌟

Subscribe to stay up-to-date and receive quality front-end development tutorials straight to your inbox!

No spam, sales, or ads. Unsubscribe anytime you wish.

© 2024, Muhi Masri