Multiple File Uploading with Progress Bar in HTML CSS and JavaScript

7

Multiple File Uploading with Progress Bar in HTML CSS JavaScript and PHP

Creating a multiple file uploader with a progress bar is an excellent project for beginner web developers. It combines basic web development concepts with practical features, offering a hands-on learning experience that is both rewarding and educational.

In this blog post, I will guide you through creating a file uploader using HTML, CSS, and JavaScript that allows users to upload multiple files by either clicking a browse button or dragging and dropping them. A progress bar and file size will update dynamically to display the uploading status.

Additionally, users will have the option to cancel the file-uploading process by clicking a cancel button. There will also be text updating users about the current file status, such as uploading, canceled, or uploaded.

Since vanilla JavaScript alone cannot handle file uploads to a server, a few lines of PHP code will be utilized to receive and store the files on the server.

Why should beginners create this project?

By creating this file-uploading project, beginners can gain the following skills:

  • HTML Basics: You’ll gain a solid understanding of form elements and file input controls.
  • CSS Styling: Learn how to style forms and progress bars to create visually appealing interfaces.
  • JavaScript Fundamentals: Discover how to manipulate DOM elements, handle events, and work with file objects.
  • PHP Integration: Get a taste of server-side scripting, an essential aspect of web development.
  • Real-World Skills: File uploading is a common feature in web applications. Mastering it prepares you for more complex projects.

Video Tutorial of File Uploading in HTML CSS & JavaScript

The YouTube video above is a great resource if you prefer learning from video tutorials. In this video, I explain each line of code and provide informative comments to make the process of creating your file uploader easy to follow, especially for beginners.

However, if you prefer reading blog posts or need a step-by-step guide for this project, you can keep reading this post.

Steps to Create File Uploader in HTML CSS & JavaScript

To implement this project, we will be using PHP, so you’ll need to set up a local server environment. Download and install XAMPP to run PHP files on your local machine. Once XAMPP is installed, follow these step-by-step instructions:

  • First, create a folder with any name you like, e.g., file-uploader. Then, create the necessary files inside it.
  • Create a file named index.html. This will serve as the main HTML file for your project.
    Create a file named style.css. This file will contain all the CSS code for styling your file uploader.
  • Create a file named script.js. This file will include the JavaScript code to handle file uploads and user interactions.
  • Create a file named api.php. This file will contain the PHP code necessary to process and store uploaded files.
  • Create a folder named uploads. This directory will store all the files uploaded via the file uploader.

Once you have finished creating the necessary files and folders for this project, paste the given code into the specified file.

To start, add the following HTML codes to your index.html file. These codes include all essential HTML elements, such as div, ul, input, and more for the project.

<!DOCTYPE html>
<!-- Coding By CodingNepal - www.codingnepalweb.com -->
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Uploader JavaScript | CodingNepal</title>
    <link rel="stylesheet" href="style.css">
    <!-- Linking Box Icon for Icons -->
    <link rel="stylesheet" href="https://unpkg.com/[email protected]/css/boxicons.min.css">
    <script src="script.js" defer></script>
  </head>
  <body>
    <div class="file-uploader">
      <div class="uploader-header">
        <h2 class="uploader-title">File Uploader</h2>
        <h4 class="file-completed-status"></h4>
      </div>
      <ul class="file-list"></ul>
      <div class="file-upload-box">
        <h2 class="box-title">
          <span class="file-instruction">Drag files here or</span>
          <span class="file-browse-button">browse</span>
        </h2>
        <input class="file-browse-input" type="file" multiple hidden>
      </div>
    </div>
  </body>
</html>

Next, add the following CSS codes to your style.css file to apply visual styling to your file uploader.

/* Importing Inter Font from Google Fonts */
@import url('https://fonts.googleapis.com/css2?family=Inter:[email protected]&display=swap');

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: "Inter", sans-serif;
}

body {
  display: flex;
  align-items: center;
  padding: 15px;
  justify-content: center;
  min-height: 100vh;
  background: #5145BA;
}

.file-uploader {
  width: 500px;
  background: #fff;
  border-radius: 5px;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
}

.file-uploader .uploader-header {
  display: flex;
  padding: 20px;
  background: #EEF1FB;
  align-items: center;
  border-radius: 5px 5px 0 0;
  justify-content: space-between;
}

.uploader-header .uploader-title {
  font-size: 1.2rem;
  font-weight: 700;
  text-transform: uppercase;
}

.uploader-header .file-completed-status {
  font-size: 1rem;
  font-weight: 500;
  color: #333;
}

.file-uploader .file-list {
  list-style: none;
  width: 100%;
  padding-bottom: 10px;
  max-height: 400px;
  overflow-y: auto;
  scrollbar-color: #999 transparent;
  scrollbar-width: thin;
}

.file-uploader .file-list:has(li) {
  padding: 20px;
}

.file-list .file-item {
  display: flex;
  gap: 14px;
  margin-bottom: 22px;
}

.file-list .file-item:last-child {
  margin-bottom: 0px;
}

.file-list .file-item .file-extension {
  height: 50px;
  width: 50px;
  color: #fff;
  display: flex;
  text-transform: uppercase;
  align-items: center;
  justify-content: center;
  border-radius: 15px;
  background: #5145BA;
}

.file-list .file-item .file-content-wrapper {
  flex: 1;
}

.file-list .file-item .file-content {
  display: flex;
  width: 100%;
  justify-content: space-between;
}

.file-list .file-item .file-name {
  font-size: 1rem;
  font-weight: 600;
}

.file-list .file-item .file-info {
  display: flex;
  gap: 5px;
}

.file-list .file-item .file-info small {
  color: #5c5c5c;
  margin-top: 5px;
  display: block;
  font-size: 0.9rem;
  font-weight: 500;
}

.file-list .file-item .file-info .file-status {
  color: #5145BA;
}

.file-list .file-item .cancel-button {
  align-self: center;
  border: none;
  outline: none;
  background: none;
  cursor: pointer;
  font-size: 1.4rem;
}

.file-list .file-item .cancel-button:hover {
  color: #E3413F;
}

.file-list .file-item .file-progress-bar {
  width: 100%;
  height: 3px;
  margin-top: 10px;
  border-radius: 30px;
  background: #d9d9d9;
}

.file-list .file-item .file-progress-bar .file-progress {
  width: 0%;
  height: inherit;
  border-radius: inherit;
  background: #5145BA;
}

.file-uploader .file-upload-box {
  margin: 10px 20px 20px;
  border-radius: 5px;
  min-height: 100px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px dashed #B1ADD4;
  transition: all 0.2s ease;
}

.file-uploader .file-upload-box.active {
  border: 2px solid #5145BA;
  background: #F3F6FF;
}

.file-uploader .file-upload-box .box-title {
  font-size: 1.05rem;
  font-weight: 500;
  color: #626161;
}

.file-uploader .file-upload-box.active .box-title {
  pointer-events: none;
}

.file-upload-box .box-title .file-browse-button {
  color: #5145BA;
  cursor: pointer;
}

.file-upload-box .box-title .file-browse-button:hover {
 text-decoration: underline;
}

Add the following JavaScript code to your script.js file. This script manages file uploads, and user interactions, updates the DOM, and provides other functionalities for the file uploader.

const fileList = document.querySelector(".file-list");
const fileBrowseButton = document.querySelector(".file-browse-button");
const fileBrowseInput = document.querySelector(".file-browse-input");
const fileUploadBox = document.querySelector(".file-upload-box");
const fileCompletedStatus = document.querySelector(".file-completed-status");

let totalFiles = 0;
let completedFiles = 0;

// Function to create HTML for each file item
const createFileItemHTML = (file, uniqueIdentifier) => {
    // Extracting file name, size, and extension
    const {name, size} = file;
    const extension = name.split(".").pop();
    const formattedFileSize = size >= 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(2)} MB` : `${(size / 1024).toFixed(2)} KB`;

    // Generating HTML for file item
    return `<li class="file-item" id="file-item-${uniqueIdentifier}">
                <div class="file-extension">${extension}</div>
                <div class="file-content-wrapper">
                <div class="file-content">
                    <div class="file-details">
                    <h5 class="file-name">${name}</h5>
                    <div class="file-info">
                        <small class="file-size">0 MB / ${formattedFileSize}</small>
                        <small class="file-divider">•</small>
                        <small class="file-status">Uploading...</small>
                    </div>
                    </div>
                    <button class="cancel-button">
                    <i class="bx bx-x"></i>
                    </button>
                </div>
                <div class="file-progress-bar">
                    <div class="file-progress"></div>
                </div>
                </div>
            </li>`;
}

// Function to handle file uploading
const handleFileUploading = (file, uniqueIdentifier) => {
    const xhr = new XMLHttpRequest();
    const formData = new FormData();
    formData.append("file", file);

    // Adding progress event listener to the ajax request
    xhr.upload.addEventListener("progress", (e) => {
        // Updating progress bar and file size element
        const fileProgress = document.querySelector(`#file-item-${uniqueIdentifier} .file-progress`);
        const fileSize = document.querySelector(`#file-item-${uniqueIdentifier} .file-size`);

        // Formatting the uploading or total file size into KB or MB accordingly
        const formattedFileSize = file.size >= 1024 * 1024  ? `${(e.loaded / (1024 * 1024)).toFixed(2)} MB / ${(e.total / (1024 * 1024)).toFixed(2)} MB` : `${(e.loaded / 1024).toFixed(2)} KB / ${(e.total / 1024).toFixed(2)} KB`;

        const progress = Math.round((e.loaded / e.total) * 100);
        fileProgress.style.width = `${progress}%`;
        fileSize.innerText = formattedFileSize;
    });

    // Opening connection to the server API endpoint "api.php" and sending the form data
    xhr.open("POST", "api.php", true);
    xhr.send(formData);

    return xhr;
}

// Function to handle selected files
const handleSelectedFiles = ([...files]) => {
    if(files.length === 0) return; // Check if no files are selected
    totalFiles += files.length;

    files.forEach((file, index) => {
        const uniqueIdentifier = Date.now() + index;
        const fileItemHTML = createFileItemHTML(file, uniqueIdentifier);
        // Inserting each file item into file list
        fileList.insertAdjacentHTML("afterbegin", fileItemHTML);
        const currentFileItem = document.querySelector(`#file-item-${uniqueIdentifier}`);
        const cancelFileUploadButton = currentFileItem.querySelector(".cancel-button");

        const xhr = handleFileUploading(file, uniqueIdentifier);

        // Update file status text and change color of it 
        const updateFileStatus = (status, color) => {
            currentFileItem.querySelector(".file-status").innerText = status;
            currentFileItem.querySelector(".file-status").style.color = color;
        }

        xhr.addEventListener("readystatechange", () => {
            // Handling completion of file upload
            if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
                completedFiles++;
                cancelFileUploadButton.remove();
                updateFileStatus("Completed", "#00B125");
                fileCompletedStatus.innerText = `${completedFiles} / ${totalFiles} files completed`;
            }
        });

        // Handling cancellation of file upload
        cancelFileUploadButton.addEventListener("click", () => {
            xhr.abort(); // Cancel file upload
            updateFileStatus("Cancelled", "#E3413F");
            cancelFileUploadButton.remove();
        });

        // Show Alert if there is any error occured during file uploading
        xhr.addEventListener("error", () => {
            updateFileStatus("Error", "#E3413F");
            alert("An error occurred during the file upload!");
        });
    });

    fileCompletedStatus.innerText = `${completedFiles} / ${totalFiles} files completed`;
}

// Function to handle file drop event
fileUploadBox.addEventListener("drop", (e) => {
    e.preventDefault();
    handleSelectedFiles(e.dataTransfer.files);
    fileUploadBox.classList.remove("active");
    fileUploadBox.querySelector(".file-instruction").innerText = "Drag files here or";
});

// Function to handle file dragover event
fileUploadBox.addEventListener("dragover", (e) => {
    e.preventDefault();
    fileUploadBox.classList.add("active");
    fileUploadBox.querySelector(".file-instruction").innerText = "Release to upload or";
});

// Function to handle file dragleave event
fileUploadBox.addEventListener("dragleave", (e) => {
    e.preventDefault();
    fileUploadBox.classList.remove("active");
    fileUploadBox.querySelector(".file-instruction").innerText = "Drag files here or";
});

fileBrowseInput.addEventListener("change", (e) => handleSelectedFiles(e.target.files));
fileBrowseButton.addEventListener("click", () => fileBrowseInput.click());

Finally, add the following PHP code to your api.php file. This script is used to receive the user-selected file and save it to a specified folder with a dynamic file name.

<?php 

// Specify the folder where files will be uploaded
$folder = "uploads/";

// Move the uploaded file to the specified folder
// $_FILES['file']['tmp_name'] is the temporary file name
// $_FILES['file']['name'] is the original file name
move_uploaded_file($_FILES['file']['tmp_name'], $folder . time() . '_' . $_FILES['file']['name']);

Once you have added all the necessary code to your files, you need to move your project folder into the htdocs folder within your XAMPP installation directory. This step is crucial because the htdocs folder is the default directory where XAMPP looks for files to serve via the local server. Typically, the htdocs folder path can be c:/xampp/htdocs.

Now you’re ready to use your file uploader. To get started, open the XAMPP Control Panel and start the Apache server. Next, open your browser and enter the URL: localhost/your-project-folder-name. For example, if your project folder is named “file-uploader,” you would go to localhost/file-uploader.

Multiple File Uploading with Progress Bar in HTML CSS JavaScript and PHP XAMPP

You can now test your file uploader by uploading files through the interface. Enjoy the functionality and features you’ve implemented, including the dynamic progress bar, file size display, and the ability to cancel uploads.

Conclusion and Final words

By completing this project, you’ve learned how to create a functional multiple-file uploader with a progress bar using HTML, CSS, and JavaScript. You also learned how to handle file uploads on the server using PHP.

This project not only reinforces your understanding of web development basics but also equips you with a practical skill that is highly relevant in many web applications.

To take this file uploader project a step further, consider adding features such as file type validation (png, jpg, mp4), size limits (< MB), and improved error handling.

Happy Coding!

Previous article10+ Best JavaScript Image Sliders With Source Code
Next articleCreate A Sidebar Menu using HTML and CSS only

7 COMMENTS

LEAVE A REPLY

Please enter your comment!
Please enter your name here