How To Upload Image Using Ajax In Express Js

How To Upload Image Using Ajax In Express Js

Image upload is one of the most useful and required feature for almost any kind of website. Node Express is one of the growing Web Framework that is used to build websites using JavaScript. We’ll see in this tutorial that how to upload image using ajax in express js.

To see that how to upload image using ajax in express js , we’ll use,

  1. Node Express Framework – Node.js Web Framework.
  2. formidable – Image upload processing package for Node.js.
  3. read-chunk – Read a chunk from a file.
  4. Image-size – Used to identify the file type.

For install Express Framework click here..

Front-end Markup

 In this step, create index.html in views directory and paste below code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>PHPKIDA- Upload image using ajax in express js</title>
    <meta name="description" content="upload image using ajax in express js." />
    <!-- Latest compiled and minified CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <style type="text/css">body {background-color: #cccccc;}</style>
</head>
<body>
    <div class="container">
        <div class="row">
            <div class="col-md-4"></div>
            <div class="col-md-4">
                <!-- Photos upload Form -->
                <form id="upload-photos" method="post" action="/upload_photos" enctype="multipart/form-data">
                    <div class="form-group">
                        <label for="photos-input">File input</label>
                        <input id="photos-input" type="file" name="photos[]" multiple="multiple" >
                        <p class="help-block">You can upload up to 3 files.</p>
                    </div>
                    <input type="hidden" name="csrf_token" value="just_a_text_field" />
                    <input class="btn btn-default" type="submit" name="Photo Uploads" value="Upload Photos" />
                </form>
                <br/>
                <!-- Progress Bar -->
                <div class="row">
                    <div class="col-md-12">
                        <div class="progress">
                            <div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%">
                                <span class="sr-only"></span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="col-md-4"></div>
        </div>
        <!-- Photos Album Container -->
        <div id="album" class="row"></div>
    </div>
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <!-- Latest compiled and minified JavaScript -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
    <script type="text/javascript" src="/assets/js/upload.js"></script>
</body>
</html>

Frontend Ajax Image Upload

In the next step, create upload.js file in public/assets/js directory:

Now to handle the file upload from the form and send those files using Ajax to the backend.

Handling Form Submission

// On form submit, handle the file uploads.
$('#upload-photos').on('submit', function (event) {
    event.preventDefault();

    // Get the files from input, create new FormData.
    var files = $('#photos-input').get(0).files,
        formData = new FormData();
    if (files.length === 0) {
        alert('Select atleast 1 file to upload.');
        return false;
    }
    if (files.length > 3) {
        alert('You can only upload up to 3 files.');
        return false;
    }
    // Append the files to the formData.
    for (var i=0; i < files.length; i++) {
        var file = files[i];
        formData.append('photos[]', file, file.name);
    }
    // Note: We are only appending the file inputs to the FormData.
    uploadFiles(formData);
});

Input file field is checked if any files are submitted, not more than 3 file. Append the file to the formData object and pass it to the uploadFiles function to send them to the server after check pass.

Uploading Files Using Ajax

/**
 * Upload the photos using ajax request.
 *
 * @param formData
 */
function uploadFiles(formData) {
    $.ajax({
        url: '/upload_photos',
        method: 'post',
        data: formData,
        processData: false,
        contentType: false,
        xhr: function () {
            var xhr = new XMLHttpRequest();

            // Add progress event listener to the upload.
            xhr.upload.addEventListener('progress', function (event) {
                var progressBar = $('.progress-bar');

                if (event.lengthComputable) {
                    var percent = (event.loaded / event.total) * 100;
                    progressBar.width(percent + '%');

                    if (percent === 100) {
                        progressBar.removeClass('active');
                    }
                }
            });

            return xhr;
        }
    }).done(handleSuccess).fail(function (xhr, status) {
        alert(status);
    });
}

The uploadFiles function takes in the formData and creates a ajax post request for submitted to the /upload_photos endpoint.

Handling the Ajax Response

/**
 * Handle the upload response data from server and display them.
 *
 * @param data
 */
function handleSuccess(data) {
    if (data.length > 0) {
        var html = '';
        for (var i=0; i < data.length; i++) {
            var img = data[i];

            if (img.status) {
                html += '<div class="col-xs-6 col-md-4"><a href="#" class="thumbnail"><img src="' + img.publicPath + '" alt="' + img.filename  + '"></a></div>';
            } else {
                html += '<div class="col-xs-6 col-md-4"><a href="#" class="thumbnail">Invalid file type - ' + img.filename  + '</a></div>';
            }
        }

        $('#album').html(html);
    } else {
        alert('No images were uploaded.')
    }
}

The handleSuccess function uses those object, loops through them and appends the HTML inside the Album Container component.

Also add a onChange event to the file input to reset the progress bar whenever there is a new file upload is started.

Progress Bar

// Set the progress bar to 0 when a file(s) is selected.
$('#photos-input').on('change', function () {
    $('.progress-bar').width('0%');
});

Final code in upload.js file

/**
 * Upload the photos using ajax request.
 *
 * @param formData
 */
function uploadFiles(formData) {
    $.ajax({
        url: '/upload_photos',
        method: 'post',
        data: formData,
        processData: false,
        contentType: false,
        xhr: function () {
            var xhr = new XMLHttpRequest();

            // Add progress event listener to the upload.
            xhr.upload.addEventListener('progress', function (event) {
                var progressBar = $('.progress-bar');

                if (event.lengthComputable) {
                    var percent = (event.loaded / event.total) * 100;
                    progressBar.width(percent + '%');

                    if (percent === 100) {
                        progressBar.removeClass('active');
                    }
                }
            });

            return xhr;
        }
    }).done(handleSuccess).fail(function (xhr, status) {
        alert(status);
    });
}

/**
 * Handle the upload response data from server and display them.
 *
 * @param data
 */
function handleSuccess(data) {
    if (data.length > 0) {
        var html = '';
        for (var i=0; i < data.length; i++) {
            var img = data[i];

            if (img.status) {
                html += '<div class="col-xs-6 col-md-4"><a href="#" class="thumbnail"><img src="' + img.publicPath + '" alt="' + img.filename  + '"></a></div>';
            } else {
                html += '<div class="col-xs-6 col-md-4"><a href="#" class="thumbnail">Invalid file type - ' + img.filename  + '</a></div>';
            }
        }

        $('#album').html(html);
    } else {
        alert('No images were uploaded.')
    }
}

// Set the progress bar to 0 when a file(s) is selected.
$('#photos-input').on('change', function () {
    $('.progress-bar').width('0%');
});

// On form submit, handle the file uploads.
$('#upload-photos').on('submit', function (event) {
    event.preventDefault();

    // Get the files from input, create new FormData.
    var files = $('#photos-input').get(0).files,
        formData = new FormData();

    if (files.length === 0) {
        alert('Select atleast 1 file to upload.');
        return false;
    }

    if (files.length > 3) {
        alert('You can only upload up to 3 files.');
        return false;
    }

    // Append the files to the formData.
    for (var i=0; i < files.length; i++) {
        var file = files[i];
        formData.append('photos[]', file, file.name);
    }

    // Note: We are only appending the file inputs to the FormData.
    uploadFiles(formData);
});

Node.js Server Script

Two method will written in app.js file

  1.  get:-  Index page which will return the index.html.
  2. post:-  The uploaded files will be processed.

Packages

First, let’s install the required packages for the application using the Node Package Manager (npm).

npm install express formidable read-chunk file-type image-size  –save

After installing the all packages put below code in app.js file

var express = require('express'), //require express library
	path = require('path'), //The path module provides utilities for working with file and directory paths.
	fs = require('fs'), //The fs module enables interacting with the file system in a way modeled on standard POSIX functions.
	formidable = require('formidable'), //A Node.js module for parsing form data, especially file uploads.
	readChunk = require('read-chunk'), //Read a chunk from a file
	fileType = require('file-type'), //This package is for detecting binary-based file formats, not text-based formats like
	sizeOf = require('image-size'); //A Node module to get dimensions of any image file

var app = express();

app.set('port',(process.env.PORT || 3000)); //set the port

//Tell express to serve stati files from the following directories
app.use(express.static('public'));
app.use('/uploads',express.static('uploads'));


//Routes will be defined here.
app.get('/',function(req,res){ //get method for display index.html

	var filesPath = path.join(__dirname,'uploads/');  //upload directory path
	fs.readdir(filesPath,function(err,files){
		if(err){
			console.log(err);
			return;
		}

		files.forEach(function(file){
			fs.stat(filesPath + file,function(err,	stats){
				if(err){
					console.log(err);
					return;
				}

				var createdAt = Date.parse(stats.ctime),
					days = Math.round((Date.now() - createdAt) / (1000*60*60*24));

				if(days > 1){
					fs.unlink(filesPath + file);
				}
			});
		});
	});



	//The res. sendFile() function basically transfers the file at the given path and it sets the Content-Type response HTTP header field based on the filename extension
	//The path.join() method joins the specified path segments into one path. You can specify as many path segments as you like. The specified path segments must be strings, separated by comma
	res.sendFile(path.join(__dirname, 'views/index.html'));
});

app.post('/upload_photos', function (req, res) { //post method for upload image in the upload directory and show on the page
    var photos = [],
    	form = new formidable.IncomingForm(); //instance for formidable

    // Tells formidable that there will be multiple files sent.
	form.multiples = true;
	// Upload directory for the images
	form.uploadDir = path.join(__dirname,'tmp_uploads');

	form.on('file',function(name,file){
		if(photos.length === 3){
			fs.unlink(file.path);
			return true;
		}

		var buffer = null,
            type = null,
            filename = '';

        buffer = readChunk.sync(file.path, 0, 262);
        type = sizeOf(file.path).type; //get the type of image

        if(type !== null & (type === 'png' || type === 'jpg' || type === 'jpeg')){
        	// Assign new file name
        	filename = Date.now() + '-' + file.name;

        	// Move the file with the new file name
        	fs.rename(file.path, path.join(__dirname, 'uploads/' + filename),() => { 
  				console.log("\nFile Uploaded!\n");
			});

        	// Add to the list of photos
        	photos.push({
        		status: true,
        		filename: filename,
        		type: type.ext,
        		publicPath: 'uploads/' + filename
        	});
        } else {
        	photos.push({
        		status: false,
        		filename: file.name,
        		message: 'Invalaid file type'
        	});
        	fs.unlink(file.path);
        }
	});

	//error msg
	form.on('error', function(err){
		console.log('Error occurred during processing - ' + err);
	});

	//Invoked when all the fields have been processed. 
	form.on('end',function(){
		console.log('All the request fields have been processed.');
	});

	//parse the incoming form fields
	form.parse(req, function(err, field, files){
		res.status(200).json(photos);
	});
});

app.listen(app.get('port'),function(){
	console.log('Express started at port '+app.get('port'));
});

Once all the files are in place, fire up the Express server using the node app.js from within the root directory and load up the browser at http://localhost:3000.

Upload Image Using Ajax

Hope you guys found this useful.

About Author

Hey, this is Nilesh Sharma and I belong to the Pink City (Jaipur). My job as a software developer has made me a patient man and negotiation my skills! My job also keeps me on my toes, as i always trying to learn new skills to keep pace with the changing technology landscape.

Sign up for weekly update

Milkshake is almost ready. If you're interested in testing it out, then sign up below to get exclusive access.