Handling file upload in Nodejs

Image source:Pixabay

Node.js is an open-source JavaScript runtime environment for developing variety of server tools and applications commonly used for creation of backend apis. In today’s tutorial we are going to handle file upload to nodejs server using express framework. We are going to continue from login and registration apis we created in the previous article which can be found here.

We are going to use multer middleware to handle file upload,async module to handle file operations and node cmd in case you need to run terminal commands in nodejs. So let’s install them by running the following command:

npm install --save multer async node-cmd

Next edit server.js file as follows:

var express    = require("express");
var login = require('./routes/loginroutes');
var upload = require('./routes/fileroutes');
var bodyParser = require('body-parser');
/*
Module:multer
multer is middleware used to handle multipart form data
*/
var multer = require('multer');
var multerupload = multer({ dest: 'fileprint/' })
var app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
var router = express.Router();
// test route
router.get('/', function(req, res) {
res.json({ message: 'welcome to our upload module apis' });
});
//route to handle user registration
router.post('/register',login.register);
router.post('/login',login.login);
//route to handle file printing and listing
router.post('/fileupload',multerupload.any(),upload.fileupload);
app.use('/api', router);
app.listen(4000);

We have imported multer module in server.js and set temporary file storage destination on lines 9 & 10.Next we created a fileupload route which will handle file upload requests to server.We have set any option in multerupload to allow uploading of any types of files. You can find other options for specific files here. Now I have created another routes file called fileroutes.js within routes folder to handle file uploading and listing tasks and called fileupload function from that file on line 31. The file structure now is as follows:

├── package.json
├── routes
│ ├── fileroutes.js
│ └── loginroutes.js
├── server.js

Now we are going to assume that user sends multiple files from frontend at a time in the form of an array. Let us now move forward to define fileupload handler in fileroutes.js file.

var fs = require("fs");
var path = require('path');
var async = require('async');
var cmd=require('node-cmd');
exports.fileprint = function(req,res){
// console.log("req",req.files);
var filesArray = req.files;
async.each(filesArray,function(file,eachcallback){
//carry out your file operations here and pass callback at the end
},function(err){
if(err){
console.log("error ocurred in each",err);
}
else{
console.log("finished prcessing");
res.send({
"code":"200",
"success":"files printed successfully"
})
cmd.run('rm -rf ./fileupload/*');
}
});
}

In this file we are going to first store files retrieved from client request in local filesArray variable.

If you print console output of req.files then it will show following format:

[ { fieldname: 'bigcalender.png',
originalname: 'bigcalender.png',
encoding: '7bit',
mimetype: 'image/png',
destination: 'fileupload/',
filename: '36d838a63e8df1f38bd94ca93fd83a9d',
path: 'fileupload/36d838a63e8df1f38bd94ca93fd83a9d',
size: 39568 } ]

‘async.each’ method take array as an input,iterate over each item and then uses callback to do processing after all items have been iterated(More detailed explanation here). We iterate through the array using async each method since it allows us to get final callback once all the files in the array have finished processing.In our case we have sent response back to client in final callback and removed temporary files created in fileupload folder by multer module.

Going a step further we can store the files in some specific path using fs module in async each module as follows:

async.each(filesArray,function(file,eachcallback){
async.waterfall([
function (callback) {
fs.readFile(file.path, (err, data) => {
if (err) {
console.log("err ocurred", err);
}
else {
callback(null,data);
}
});
},
function (data, callback) {
var writepath = "/home/user/Documents";
fs.writeFile(writePath + file.originalname, data, (err) => {
if (err) {
console.log("error occured", err);
}
else {
callback(null, 'done');
}
});
}
], function (err, result) {
// result now equals 'done'
//pass final callback to async each to move on to next file
eachcallback();
});
},function(err){
if(err){
console.log("error ocurred in each",err);
}
else{
console.log("finished prcessing");
...
}
});

In this case we have used async waterfall method within async.each to execute series of tasks synchronously. Async waterfall method takes array of functions as an input, runs them one after another and handles final callback once all functions have finished execution synchronously. One key thing to keep in mind is async.each operation iterates through array items parallely while waterfall ensures that each parallel iteration will first readlfile from file path and then the file is written to writepath. Simply speaking even if you pass same 10 files to server multiple times the order in which they will be processed will differ each time however every time the server will first read the file data and then write it to file path.

You can find the complete fileroutes.js code in github gist uploaded here.

Bonus Tip:

You can modify the async.waterfall method to store files in database or server storage.

I have written a tutorial using reactjs to create simple frontend website which sends files to node server which can be found here.

Connect Deeper:

In the next tutorial I will cover how to store and retrieve the files to cloud storage in nodejs using openstack and docker, so follow our facebook page to get notified about similar posts in the future: Technoetics or simply follow our publication on medium.

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.