Whether you're building an image gallery, video-sharing platform, document management system, or any application involving file handling, enabling multiple file uploads is a valuable feature.

The biggest benefit is the user experience. Uploading files one by one is time-consuming and tedious, so by allowing users the convenience of uploading multiple files simultaneously using JavaScript  (with the visual feedback of progress indicators), you save them time,  enhance productivity, and guarantee user satisfaction.

Where Can Multiple File Uploads Help?

Multiple file uploads have broad applicability across web applications, enhancing user experience and efficiency for pretty much any task that requires them to handle files: Image Galleries and Portfolios, document management systems (contracts, invoices, reports, etc.), video sharing platforms like Youtube and Vimeo, cloud storage services (AWS S3 and similar), and of course, online forms for pretty much any use case that requires users to attach images.

In this guide, you’ll learn two things. First, we’ll see how you can upload multiple images and videos using JavaScript, and then, we’ll explore a faster, more streamlined approach using ImageKit—a powerful and easy-to-use Digital Asset Management system that provides SDKs and APIs that allow users to upload images and videos in an easier, more secure, and granular manner.

Let’s get started!

Uploading Multiple Files Using JavaScript

First, let’s cover uploading our files via JavaScript, to a server that can handle file uploads.

We’ll start by creating an HTML form with multiple input elements, then add event listeners to capture the files selected by the user. We’ll store the files and their metadata in a new FormData object, then send an AJAX request to the server via XMLHttpRequest to finally upload the files.

This is what we’ll be building.

💡
The assumption is that the server that receives the upload request can handle file uploads. This article does not cover the server-side code to handle file uploads and process data.

Step 1: Create an HTML form with a file input element that allows Multiple file selections

<form id="uploadForm" enctype="multipart/form-data">
  <input type="file" id="fileInput" name="files[]" multiple accept="image/*,video/*"/>
  <button type="button" id="uploadButton">Upload Files</button>
</form>

The provided HTML code creates a simple form with a file input element that allows users to select multiple files. The enctype property being set to "multipart/form-data" enables the <input> elements to actually upload file data, the multiple attribute on the <input type="file"> element enables multi-file selection, and optionally, setting the accept property to “image/*” and “video/*” lets the user only upload files of those types.

Step 2: Add an event listener to capture file selection

// Add event listener to the button element
const uploadButton = document.getElementById("uploadButton");
uploadButton.addEventListener("click", uploadFiles);

When our form’s upload button is clicked, the event listener will trigger the "uploadFiles" function, which is responsible for handling the file upload process, including preparing the files for upload and sending them to the server. Let’s code that function next.

Step 3: Use FormData to Store Files

function uploadFiles(event) {
  event.preventDefault();
  const fileInput = document.getElementById("fileInput");
  const selectedFiles = fileInput.files;
  // Check if any files are selected
  if (selectedFiles.length === 0) {
    alert("Please select at least one file to upload.");
    return;
  }
}

First, we get the files that were selected (with some error-handling logic to cover the case of no files being selected).

Then, we use the JavaScript FormData object to store them.

function uploadFiles() {
  // ...
  // Create a FormData object to store the form data
  const formData = new FormData();
  // Append each selected file to the FormData object
  for (let i = 0; i < selectedFiles.length; i++) {
    formData.append("files[]", selectedFiles[i]);
  }
}

Now, our files are prepped for sending to the server via an AJAX request.

If you were to console.log the FormData, using the spread operator:

// Display the FormData structure 
console.log([...formData])

You’d see that the FormData is structured like this:

[  
  ["files[]",    [object File] ],
  ["files[]",    [object File] ],
  ["files[]",    [object File] ]
]

An array of tuples, the first pair being a key called “files[]” and its value being a File object.To see the contents of the value in this pair, we could do something like this:

  // Display full FormData content
  for (const pair of formData.entries()) {
    console.log(
      pair[0],
      pair[1].name + ", " + pair[1].size + ", " + pair[1].type + ", " + pair[1].lastModifiedDate
    );
  }

Now, you’ll be able to see exactly what happened with the files that you just selected for upload. This formData object includes each file’s size in bytes, MIME type, and the timestamp for when it was last modified.

files[], vacation-1.jpg, 297796, image/jpeg, 2023-08-28T23:39:30.657Z
files[], vacation-lossless.png, 699409, image/png, 2023-08-24T18:00:32.000Z
files[], funny-cat.mp4, 421208, video/mp4, 2023-08-22T17:53:14.218Z

When we send this data as an AJAX request, it iterates over this array, sending each file to your upload endpoint one at a time.

Step 4: Send the AJAX request using XMLHttpRequest

const xhr = new XMLHttpRequest();
xhr.open("POST", "/uploadFiles", true);
xhr.onreadystatechange = function () {
  if (xhr.readyState === XMLHttpRequest.DONE) {
    if (xhr.status === 200) {
       // Handle successful response from the server
      console.log('Files uploaded successfully!');
      alert("Files uploaded successfully!");
    } else {
       // Handle error response from the server
      console.error('Failed to upload files.');
     alert("Error occurred during file upload. Please try again.");
    }
  }
};
xhr.send(formData);

The XMLHttpRequest object creates and sends the AJAX request. The request method is set to "POST", the URL points to the endpoint that handles file uploads (we’re hitting the “/uploadFiles" endpoint on the server, in this case), and our FormData object is the request payload sent.

You’ll be able to see exactly what this code does with the files you selected for upload.

Now that you’ve seen the hard way of doing things, let’s look at an easier approach – with ImageKit.

Uploading Multiple Files Using ImageKit’s SDK

ImageKit is a real-time image and video storage, transformation, optimization, and delivery platform. It provides APIs and SDKs that streamline multi-file uploads from both the client and the server, with granular control over every file upload request.

As you’ve seen in the previous code example, there’s considerable work involved in uploading multiple files using JavaScript. You need knowledge of browser APIs, how to compose data with the FormData() interface, and how to make AJAX calls.

All of this is simplified for you when using ImageKit’s JavaScript SDK. It abstracts away all that complexity into a single upload() method call, making it easier to securely use ImageKit’s Upload API directly from your client-side code, to upload multiple images and videos in one go to ImageKit’s S3-backed asset management solution – the  Media Library.

Here are some additional features you get when using the SDK :

  1. Iterative Uploads: ImageKit allows you to upload files iteratively or in batches. It is useful when you have a large number of files to upload and want to manage the process in smaller chunks to avoid potential timeouts or performance issues.
  2. Granular Control:The ImageKit SDK allows you to pass additional parameters during upload to add custom metadata, set tags, and specify visibility and access control for each uploaded file.
  3. Asynchronous Processing: Uploading multiple files can be time-consuming, especially when dealing with large files or slow connections. ImageKit processes these uploads asynchronously, meaning your web application can continue functioning without waiting for multiple uploads to complete or suffering from the entire upload process being aborted when one upload fails.
  4. URL-Based Transformations: After uploading your media assets with ImageKit's API, you can apply various real-time transformations – cropping, resizing, overlays, and more – to your uploaded images and videos. This feature allows you to avoid expensive processing, and serve the transformed assets directly, as, when, and how you want to use them.

Let’s take a look at how we can use ImageKit's JavaScript SDK to upload multiple images with granular control:

Step 1: Create an ImageKit account

First of all, create an ImageKit account and get your API Keys (public and private) from the Dashboard here.

Step 2: Install ImageKit’s JavaScript SDK

You can either install it using NPM…

npm install imagekit-javascript --save
#or
yarn add imagekit-javascript

…Or, the more convenient option – include it as a statically compiled package in your page source, within a <script> tag.

<script type="text/javascript" src="https://unpkg.com/imagekit-javascript@1.3.0/dist/imagekit.min.js"></script>

To prevent breaking changes, it is recommended you specify an exact version of the SDK from the Unpkg CDN, or self-host the version you want.

But if you know what you’re doing, the latest version is always available here (just omit the version number).

<script type="text/javascript" src="https://unpkg.com/imagekit-javascript/dist/imagekit.min.js"></script>

Now, it’s time to handle file upload logic using the ImageKit JavaScript SDK.

Step 3: Ensure server-side security parameter generation

ImageKit uses signature-based authentication for secure client-side uploads. Before proceeding, make sure you have a server that can generate and then use:

a)  a unique token (V4 UUID) and
b) an epoch time (in seconds) indicating the expiration of the signature (within 1 hour).

…to calculate and return an HMAC-SHA1 digest based on the concatenation of these inputs. If you’re using a Node.js-based server like Express, you can use the ImageKit SDK for Node.js (use your private key here) on your server to simplify this process.

Your client-side code will make an HTTP GET request to a server-side endpoint to get these security parameters (signature, token, and expire) as a JSON in this format:

{
    token: "1bab386f-45ea-49e1-9f0d-6afe49a5b250",
    expire: 1580372696,
    signature: "0f9d5a45e97c24fa9200a9d5543c9af1e2c45a54"
}

This article won’t cover the server-side code, so make sure you implement it before proceeding.

Step 4: The Client-side Code

Your form code can remain unchanged, just make sure you include the ImageKit SDK.

<form id="uploadForm" enctype="multipart/form-data">
  <input type="file" id="fileInput" name="files[]" multiple accept="image/*,video/*"/>
  <button type="button" id="uploadButton">Upload Files</button>
</form>

<script type="text/javascript" 
src="https://unpkg.com/imagekit-javascript@1.3.0/dist/imagekit.min.js"></script>
// Before anything, instantiate the ImageKit SDK
const imageKit = new ImageKit({
        publicKey: "your_public_api_key",
        urlEndpoint: "https://ik.imagekit.io/your_imagekit_id",
    });

// Add event listener to the button element
const uploadButton = document.getElementById("uploadButton");
uploadButton.addEventListener("click", async () => {
  try {
    // Before uploading, fetch the token, expire, and signature from your server
    const response = await fetch("your_server_api_endpoint");
    const data = await response.json();

    // Check if the server API call was successful and if all required security credentials are present
    if (response.ok && data.token && data.expire && data.signature) {
      const fileInput = document.getElementById("fileInput");
      const selectedFiles = fileInput.files;

      // Then, iterate over the list, uploading the files
      for (const file of selectedFiles) {
        // Custom upload options for each file
        const uploadOptions = {
          filename: file.name, // required
          signature: data.signature, // required, use the fetched signature
          token: data.token, // required, use the fetched token
          expire: data.expire // required, use the fetched expire
        };
        // Upload using those options
        imagekit
          .upload(file, uploadOptions)
          .then((result) => {
            console.log("Upload successful! Public URL : " + result.url);
          })
          .catch((error) => {
            console.log("Upload failed. Error: " + error);
          });
      }
    } else {
      console.error("Failed to fetch security credentials from the server.");
    }
  } catch (err) {
    console.error("Error while contacting the server: ", err);
  }
});

That’s it! Just bear in mind that on the free tier, the maximum file size supported is 25MB (per file). However, this does go up to 300 MB for video if you’re on one of the paid plans.

Custom Upload Options Explained

Other than fileName, signature, token, and expire,  which are mandatory for uploads, you can provide various optional parameters to customize the upload behavior.

Here are some upload parameters that ImageKit supports:

  1. Folder (folder): Specify the folder path to store the file on the ImageKit server. Organizing files in folders helps to keep your media assets structured.
  2. Tags (tags): Tags are user-defined metadata you can attach to the uploaded file. Tags help in categorizing and searching for files later based on specific criteria.
  3. Custom Coordinates (customCoordinates): Used for image cropping. It allows you to specify custom coordinates (x, y, width, height) to define a rectangular region for cropping the image during upload.
  4. Use Unique Filename (useUniqueFileName): If set to true, ImageKit will generate a unique file name for the uploaded file to avoid naming conflicts. It is useful when you want to ensure unique filenames for each upload.
  5. Response Fields (responseFields): This is a field of comma-separate variables that helps you request metadata in the API response in a fine-grained manner. This helps in reducing unnecessary data transfer, and improves the response time.

Here's an example of how you can use these parameters during the upload process:

//...
 for (const file of selectedFiles) {
        // Custom upload options for each file
        const uploadOptions = {
          filename: file.name, // required
          signature: data.signature, // required, use the fetched signature
          token: data.token, // required, use the fetched token
          expire: data.expire, // required, use the fetched expire
          folder: "/uploads/my-pets",
          tags: ["images", "animals"],
          customCoordinates: "10,10,300,300", // x, y, width, height
          useUniqueFileName: true,
          responseFields: "tags, customCoordinates, isPrivateFile" // get the value of tags, customCoordinates, and isPrivateFile in the response body
        };

        // Upload using those options
        imagekit.upload(file, uploadOptions)
     }

Upload API Response Explained

After uploading a file to ImageKIt, the service returns a JSON-encoded response containing information about the uploaded file. The JSON data contains details such as file URL, file ID, size, type, and other data related to the uploaded file – and you can persist these details (in particular: fileId and the public URL)  in your database for future use in your application.

Here's an example of the JSON response returned by ImageKit:

{
  "fileId": "your_file_id",
  "name": "your_file_name.jpg",
  "url": "https://your_imagekit_domain.com/your_file_path.jpg",
  "size": 1024,
  "height": 800,
  "width": 1200,
  "format": "jpg",
  "tags": ["tag1", "tag2"],
  "customCoordinates": "10,10,300,300",
  "isPrivateFile": false,
  "metadata": { /* custom metadata, if any */ },
  "thumbnail": "https://your_imagekit_domain.com/your_thumbnail_path.jpg",
  "thumbnailHeight": 200,
  "thumbnailWidth": 300
}

To see a complete list of the fields in the response body, see here.

Streamlining Media Management for Developers

ImageKit's array of developer-friendly APIs and SDKs makes the process of uploading, managing, transforming, and optimizing your images and videos remarkably straightforward.

Here's a glimpse of ImageKit's versatile APIs:

  • File Upload API: Upload images and videos to the ImageKit server with a myriad of customizable parameters like file name, folder, tags, and custom coordinates.
  • Metadata API: Manage image and video metadata, including details such as height, width, format, and any custom metadata associated with the image/video.
  • Media API: Effectively manage your media assets programmatically, with options to upload, search, update, rename, transfer, delete, list, or obtain detailed file information either individually, or in bulk.

ImageKit's comprehensive SDKs cater to both client-side and server-side development:

Conclusion

In this guide, we have delved into the world of multiple file uploads using JavaScript, presenting methods to optimize both the user and the developer experience, saving valuable time for both. We covered essential topics, such as form creation, FormData utilization for multi-file uploads to a server, and saw how ImageKit, a powerful platform offering granular control and a plethora of features through robust APIs and SDKs, streamlines multiple file uploads with its JavaScript SDK.

By implementing the code outlined here, you can significantly optimize the upload story for your applications, whether they are image galleries, document management systems, or content-rich platforms.

By combining the power of JavaScript with ImageKit's versatile APIs and SDKs, developers can streamline their bulk/batched uploads and digital asset management and also optimize and transform said assets at will after upload – all while ensuring a great user experience for technical and non-technical users alike.

Whether you are building a media-centric application, managing documents, or operating content-heavy platforms, ImageKit offers the tools you need to thrive in the digital landscape.

Sign up for our forever-free account, and get 20GB of free media storage and video processing units today!