Skip to main content

Upload Multiple Image File with Node.js

In our previous Node.js tutorial, we have explained how to upload files to Amazon S3 server using Node.js. In this tutorial, we will explain how to upload multiple images using Node.js.

Image upload is an important feature of web applications to allow users to upload profile picture, manage Gallery etc. As we mostly use server side solutions to upload images, but now you can upload images using client end solutions with Node.js, a cross-platform run-time for running server-side JavaScript applications.

We will use Node.js Express framework, formidable, read-chunk and file-type Node.js modules to handle image upload with Node.js.

We will also use Bootstrap for design and jQuery for handling Ajax to upload files. The tutorial explained in easy steps to create multiple image upload example with Node.js and also link t download running source code of example.

Also, read:

So let’s start developing application step by step to upload multiple files with progress bar using Node.js. We will have following file structure for this example:

  • multiple_file_upload_nodejs
    • node_modules – This file contains the Node.js packages.
    • uploads – Image upload directory.
    • app.js – This file contains the Node.js Express server code.
    • public – The directory contains static asset files.
      • assets
        • js
          • ajaxUpload.js – The file contains the front-end Ajax file upload functions.
    • views – The directory contains the view templates/files.
      • index.html – Main HTML file for image upload form and display upload images.
Step1: Configure Node.js Application

We will configure Node.js project by creating a project directory and change into that directory. Here we will create project directory multiple_file_upload_nodejs.

Then we will install following required Node.js packages for the application using the Node Package Manager (npm).

npm install express formidable read-chunk file-type --save

then we will create Node.js project structure as explained above.

Step2: Create Multiple Image Upload Form

In index.html file, we will create multiple image upload Form using Bootstrap with action upload_photos. We will also design file upload progress and container to display uploaded images.

<div class="container">		
	    <h2>Example: How to Upload Multiple Images in Node.js</h2>	
		<br/><br/>	
	    <!-- uploaded photos Container -->
		<div class="row">
			<div class="col-md-12">			
				<div id="uploadedPhotos" class="row"></div>
			</div>
		</div>		
		<div class="row">
			<div class="col-md-4"></div>
			<div class="col-md-12">			
				<!-- Progress Bar -->
				<div class="row">
					<div class="col-md-4">
						<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>			
				<!-- Photos upload Form -->
				<form id="upload-photos" method="post" action="/upload_photos" enctype="multipart/form-data">
					<div class="form-group">
						<label for="photos-input">Browse Multiple Images to Upload</label>
						<input id="photos" type="file" name="photos[]" multiple="multiple" >						
					</div>
					<input type="hidden" name="csrf_token" value="just_a_text_field" />
					<br/>
					<input class="btn btn-primary" type="submit" name="Photo Uploads" value="Upload" />
				</form>
				<br/>				
			</div>
			<div class="col-md-4"></div>
		</div>		
		<br/><br/>	
	</div>
Step2: Handle Image Upload Form Submit

In ajaxUpload.js file, we will handle file upload form submit and store browse input files into formData object and pass to uploadPhotos() function to upload images using Ajax.

$('#upload-photos').on('submit', function (event) {
    event.preventDefault();
    var files = $('#photos').get(0).files,
        formData = new FormData();
    if (files.length === 0) {
        alert('Select atleast 1 file to upload.');
        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);
    }
	// Handle Photos Upload
	uploadPhotos(formData);
});
Step3: Make Image Upload Ajax Request

Now in function uploadPhotos(), we will handle functionality to make Ajax request to the /upload_photos endpoint by passing the form data to upload images. We will also handle to update image upload progress bar and show upload images when upload done using function showPhotos().

function uploadPhotos(formData) {
    $.ajax({
        url: '/upload_photos',
        method: 'post',
        data: formData,
        processData: false,
        contentType: false,
        xhr: function () {
            var xhr = new XMLHttpRequest();
            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(showPhotos).fail(function (xhr, status) {
        alert(status);
    });
}
Step4: Multiple Image Upload with Node.js Server Script

Now finally we will handle multiple image upload in Node.js script app.js. We will create two end point, one will be a get request to the page index which will return the index.html to display image upload form. The second end point will accept a post request method with upload_photos action in which the uploaded images will be processed. The file upload handled with formidable to store upload images details, readChunk to get a chunk of the file to identify the file type and pass that buffer to the file-type to get the type of the file to allow upload of png, jpg or jpeg. This is a final app.js file.

var express = require('express'),
    path = require('path'),
    fs = require('fs'),
    formidable = require('formidable'),
    readChunk = require('read-chunk'),
    fileType = require('file-type');
	
var app = express();
app.set('port', (process.env.PORT || 3000));

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

// index route
app.get('/', function (req, res) {
    var filesPath = path.join(__dirname, 'uploads/');
    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);
                }
            });
        });
    });
    res.sendFile(path.join(__dirname, 'views/index.html'));
});

// Upload photos 
app.post('/upload_photos', function (req, res) {
    var photos = [],
        form = new formidable.IncomingForm();
    form.multiples = true;
    form.uploadDir = path.join(__dirname, 'tmp_uploads');
    form.on('file', function (name, file) {        
		var buffer = null,
            type = null,
            filename = '';
        buffer = readChunk.sync(file.path, 0, 262);
        type = fileType(buffer);
        // Check the file type as it must be either png,jpg or jpeg
        if (type !== null && (type.ext === 'png' || type.ext === 'jpg' || type.ext === 'jpeg')) {
            filename = Date.now() + '-' + file.name;
            fs.rename(file.path, path.join(__dirname, 'uploads/' + filename));
            photos.push({
                status: true,
                filename: filename,
                type: type.ext,
                publicPath: 'uploads/' + filename
            });
        } else {
            photos.push({
                status: false,
                filename: file.name,
                message: 'Invalid file type'
            });
            fs.unlink(file.path);
        }
    });
    form.on('error', function(err) {
        console.log('Error occurred during processing - ' + err);
    });
    form.on('end', function() {
        console.log('All the request fields have been processed.');
    });
    form.parse(req, function (err, fields, files) {
        res.status(200).json(photos);
    });
});
app.listen(app.get('port'), function() {
    console.log('Express started at port ' + app.get('port'));
});

You may also like:

You can download the source code of running example from below link.
Download