PROWARE technologies
PROWARE technologies

Drag-and-Drop File Upload Using AJAX and Node.js (Express v.4.x)

This article details how to upload files via drag-and-drop using HTML, CSS, JavaScript, AJAX and Node.js. It will upload immediately after the user selects images to upload or by dragging and dropping them on the drop zone. It also shows a progress bar for the upload. If not familiar with JavaScript then read this tutorial which covers a version of JavaScript compatible with most Internet browsers. If not familiar with Node.js then read this tutorial which covers installing Node.js, Express.js and more.

Node.js Installation

This article will use an installation of Ubuntu Linux v.18.04 to proceed with. It can safely be installed on a virtual machine if hardware is not available.

First, create a directory to work from. From a command prompt/terminal: mkdir drag-and-drop and cd drag-and-drop.

If not already at the terminal, open up a terminal window. Execute node --version. This will show if Node.js is already installed on the machine.

If Node.js is not already installed, then issue a sudo apt install nodejs command which will install it. This can also be used to update it.

One of the technologies that the text editor Visual Studio Code was designed for is Node.js. Use the Ubuntu Software library to install Visual Studio Code.

Note that if working on a Linux server then all these commands can be executed from a terminal just make sure to be in the project folder when executing them.

Installing Express v.4.x and Nodemailer for Node.js

Express and Express-Fileupload are a requirement for this example.

Open Visual Studio Code and open the drag-and-drop folder. To install NPM Express with Fileupload, issue a sudo apt-get install npm, npm install express and npm install express-fileupload from the Terminal window in Visual Studio Code. If not using Visual Studio Code just make sure that these installation commands are issued in the project folder. The command npm init is optional and should be done before installing Express and Express-Fileupload.

Code Examples

Create a file named index.js. Then create a folder named public and create a file in it named index.html. Paste (or download) the following code into the index.js file.

const express = require("express");
const upload = require("express-fileupload");
const path = require("path");

const app = express();


// middleware
app.use(express.json());
app.use(express.urlencoded( { extended: false } )); // this is to handle URL encoded data
app.use(upload());



// enable static files pointing to the folder "public"
app.use(express.static(path.join(__dirname, "public")));



// HTTP POST
// upload image files to server
app.post("/upload", function(request, response) {
    var images = new Array();
    if(request.files) {
        var arr;
        if(Array.isArray(request.files.filesfld)) {
            arr = request.files.filesfld;
        }
        else {
            arr = new Array(1);
            arr[0] = request.files.filesfld;
        }
        for(var i = 0; i < arr.length; i++) {
            var file = arr[i];
            if(file.mimetype.substring(0,5).toLowerCase() == "image") {
                images[i] = "/" + file.name;
                file.mv("./public" + images[i], function (err) {
                    if(err) {
                        console.log(err);
                    }
                });
            }
        }
    }
    // give the server a second to write the files
    setTimeout(function(){response.json(images);}, 1000);
});

// set port from environment variable, or 8080
const PORT = process.env.PORT || 8080;

app.listen(PORT, () => console.log(`listening on port ${PORT}`));

Next, paste (or download) the following code into the file named index.html. Have the index.js file open and selected. Run the index.js code file by hitting Ctrl+F5 in Visual Studio Code or by entering the command node index.js at the command prompt/terminal. Next, open a browser and navigate to http://ipaddress:8080/. "ipaddress" can be the ip address of the machine or it can be "localhost" if working on the machine itself.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Drag-n-drop File Uploading</title>
    <style>
        *, *::before, *::after {
            box-sizing: border-box;
        }
        body {
            padding: 0;
            margin: 0;
        }
        img {
            display: block;
            margin: 0 auto;
            width: 100%;
            height: auto;
        }
        #fileslbl {
            display: flex;
            align-items: center;
            justify-content: center;
            height: 25vh;
            background-color: red;
            color: white;
            padding: 16px;
            text-align: center;
            transition: .5s;
			animation-duration: 1s;
			animation-fill-mode: forwards;
			animation-iteration-count: infinite;
        }
        #filesfld {
            display: none;
        }
        #progress {
            width: 0;
            background-color: blue;
            color: white;
            font-weight: bold;
            font-size: 14px;
            line-height: 25px;
            padding: 0 5px;
        }
		@keyframes dropbox {
			0% {
				background-image: repeating-linear-gradient(30deg, green 1%, green 3%, darkgreen 5%, darkgreen 5%);
			}
			50% {
				background-image: repeating-linear-gradient(30deg, darkgreen 1%, darkgreen 3%, green 5%, green 5%);
			}
			100% {
				background-image: repeating-linear-gradient(30deg, green 1%, green 3%, darkgreen 5%, darkgreen 5%);
			}
		}
    </style>
    <script>
        function stopDefault(event) {
            event.preventDefault();
            event.stopPropagation();
        }
        function dragOver(label, text) {
            /* ADD ALMOST ANY STYLING YOU LIKE */
			label.style.animationName = "dropbox";
            label.innerText = text;
        }
        function dragLeave(label) {
            /* THIS SHOULD REMOVE ALL STYLING BY dragOver() */
			var len = label.style.length;
            for(var i = 0; i < len; i++) {
				label.style[label.style[i]] = "";
            }
            label.innerText = "Click to choose images or drag-n-drop them here";
        }
        function addFilesAndSubmit(event) {
            var files = event.target.files || event.dataTransfer.files;
            document.getElementById("filesfld").files = files;
            submitFilesForm(document.getElementById("filesfrm"));
        }
        function submitFilesForm(form) {
            var label = document.getElementById("fileslbl");
            dragOver(label, "Uploading images..."); // set the drop zone text and styling
            var fd = new FormData();
            for(var i = 0; i < form.filesfld.files.length; i++) {
                var field = form.filesfld;
                fd.append(field.name, field.files[i], field.files[i].name);
            }
            var progress = document.getElementById("progress");
            var x = new XMLHttpRequest();
            if(x.upload) {
                x.upload.addEventListener("progress", function(event){
                    var percentage = parseInt(event.loaded / event.total * 100);
                    progress.innerText = progress.style.width = percentage + "%";
                });
            }
            x.onreadystatechange = function () {
                if(x.readyState == 4) {
                    progress.innerText = progress.style.width = "";
                    form.filesfld.value = "";
                    dragLeave(label); // this will reset the text and styling of the drop zone
                    if(x.status == 200) {
                        var images = JSON.parse(x.responseText);
                        for(var i = 0; i < images.length; i++) {
                            var img = document.createElement("img");
                            img.src = images[i];
                            document.body.appendChild(img);
                        }
                    }
                    else {
                        // failed - TODO: Add code to handle server errors
                    }
                }
            };
            x.open("post", form.action, true);
            x.send(fd);
            return false;
        }
    </script>
</head>
<body>
    <form id="filesfrm" action="/upload" method="post" onsubmit="return submitFilesForm(this);">
        <input type="file" name="filesfld" id="filesfld" accept="image/*" onchange="submitFilesForm(this.form);" multiple />
        <label for="filesfld" id="fileslbl"
        ondragover="stopDefault(event);dragOver(this, 'Drop the images to upload them.');"
        ondragenter="stopDefault(event);dragOver(this, 'Drop the images to upload them.');"
        ondragleave="stopDefault(event);dragLeave(this);"
        ondrop="stopDefault(event);dragLeave(this);addFilesAndSubmit(event);">Click to choose images or drag-n-drop them here</label>
    </form>
    <div style="text-align: left;"><div id="progress"></div></div>
</body>
</html>

Coding Videos

https://youtu.be/NGPBRqkrWP8

https://youtu.be/cLEXsGc9IUI

Here is a video on installing PM2 (Process Manager 2) to run the Node.js app when the machine starts.

https://youtu.be/UkL75knjcCY

This is the end of this article.