We will learn to do file upload with angularjs and nodejs. These can be seen as two separate parts, so for example, if you are working on AngularJS
You are going good with your development work and then you have to do a file upload, oops a hurdle. File upload is not as difficult as some people presume it to be. Well not at-least after this tutorial. Let’s flick the hurdle out of our way.
There are two parts of file upload, the client end where we should enable the user to choose a file and send it to the server. At the server, we receive the file and save it into our desired path.
In this article we will learn to do file upload with angular and node. These can be seen as two separate parts, so for example, if you are working on AngularJS with some other back-end i.e. not Node.js, you can still take help from this article for the angular part of it and vice versa.
This article assumes you have already worked with AngularJS and Node expressjs and have a basic knowledge of them.
We will be dividing this into two sections, server side with node and the client end with AngularJS.
We will use multer to handle file upload in our express app. Multer is a popular NodeJS middleware package for handling file uploads. Lets have a look at our complete Server file, I’ll explain parts of it later.
app.js
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var multer = require('multer');
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "http://localhost");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
/** Serving from the same express Server
No cors required */
app.use(express.static('../client'));
app.use(bodyParser.json());
var storage = multer.diskStorage({ //multers disk storage settings
destination: function (req, file, cb) {
cb(null, './uploads/')
},
filename: function (req, file, cb) {
var datetimestamp = Date.now();
cb(null, file.fieldname + '-' + datetimestamp + '.' + file.originalname.split('.')[file.originalname.split('.').length -1])
}
});
var upload = multer({ //multer settings
storage: storage
}).single('file');
/** API path that will upload the files */
app.post('/upload', function(req, res) {
upload(req,res,function(err){
if(err){
res.json({error_code:1,err_desc:err});
return;
}
res.json({error_code:0,err_desc:null});
})<br />
});
app.listen('3000', function(){
console.log('running on 3000...');
});
package.json
{
"name": "expapp",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"gulp": "3.9.0",
"gulp-develop-server": "0.5.0",
"gulp-jshint": "1.12.0"
},
"dependencies": {
"body-parser": "1.14.1",
"express": "4.13.3",
"fs": "0.0.2",
"multer": "1.1.0"
}
}
To install dependencies just run npm install
. Alternatively you can install each module independently and save it to your package.json
.
This section consist explanation of each block of code in our app.js
file.
At the top we are requiring our node modules.
app.js
app.use(function(req, res, next) { //allow cross origin requests
res.setHeader("Access-Control-Allow-Methods", "POST, PUT, OPTIONS, DELETE, GET");
res.header("Access-Control-Allow-Origin", "http://localhost");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
/** Serving from the same express Server
No cors required */
app.use(express.static('../client'));
app.use(bodyParser.json());
Here we are doing two things, we are allowing our express server to accept cross-origin request from another server. (In this case localhost:80)
Alternatively we are asking express to expose client folder as a static path, in this way we can run our AngularJS client code on the same express server (cross-origin wont be required if we follow this).
app.js
var storage = multer.diskStorage({ //multers disk storage settings
destination: function (req, file, cb) {
cb(null, './uploads/')
},
filename: function (req, file, cb) {
var datetimestamp = Date.now();
cb(null, file.fieldname + '-' + datetimestamp + '.' + file.originalname.split('.')[file.originalname.split('.').length -1])
}
});
Here we are defining Multer storage settings. Multer supports two type of storage, viz. memory and disk. We are using diskStorage
for this tutorial, as memory storage might give problems if the file is too large or multiple files are uploaded very fast.
In the storage setting we give the destination path to save our files. We also rename our file. I’m appending datetime to the name in order to avoid any duplicate naming conflict. Also we need to append the file extension as by default Multer would save the file without any extension.
app.js
var upload = multer({ //multer settings
storage: storage
}).single('file');
Now we create a Multer instance by calling multer
and passing our options into it. At the same time we specify the type of upload, that is, if it ismultiple files or single. In our case its single, and the parameter ('file'
) should normally be the name of the input field in our html form but in our case since we are using ngFileUpload in AngularJS it should match the key which holds the file object in the post request.
app.js
/** API path that will upload the files */
app.post('/upload', function(req, res) {
upload(req,res,function(err){
if(err){
res.json({error_code:1,err_desc:err});
return;
}
res.json({error_code:0,err_desc:null});
})
});
app.listen('3000', function(){
console.log('running on 3000...');
});
Next we create an express route as '/upload'
to upload our file. Multer also provides a callback in which we can check if there was an error while performing upload.
Finally we are creating our express server.
Each uploaded file object contains the following information.
To start the express server, go to your working directory in cmd
and run node app.js
. I’ll also bundle up the gulpfile.js
with the downloaded code, so if you use gulp for automation, you can just start the server by running gulp
.
This wraps up the work on backend, now lets move to the front-end stuff.
We will be using ng-file-upload module in AngularJS for facilitating file uploads.
Along with angular.js
we will need to include ng-file-upload
related files into our project.
Install ng-file-upload
using a package manager or download the required files form here.
Include the files into your project.
index.html
<script src="angular.min.js"></script>
<script src="ng-file-upload-shim.min.js"></script> <!-- for no html5 browsers support -->
<script src="ng-file-upload.min.js"></script>
Lets have a look at our complete code then I’ll explain each code block in detail. I’ve two files index.html
for the markup and main.js
for our angular module and controller.
index.html
<html>
<head>
<title>Home</title>
</head>
<body ng-app="fileUpload">
<h1>Angular Node File Upload</h1>
<form ng-controller="MyCtrl as up" name="up.upload_form">
Single Image with validations
<input
type="file"
ngf-select
ng-model="up.file"
name="file"
ngf-pattern="'image/*'"
accept="image/*"
ngf-max-size="20MB"
/>
Image thumbnail: <img style="width:100px;" ngf-thumbnail="up.file || '/thumb.jpg'"/>
<i ng-show="up.upload_form.file.$error.required">*required</i><br>
<i ng-show="up.upload_form.file.$error.maxSize">File too large
{{up.file.size / 1000000|number:1}}MB: max 20M</i>
<button type="submit" ng-click="up.submit()">submit</button>
<p>{{up.progress}}
</form>
</body>
<script type="text/javascript" src="node_modules/angular/angular.min.js"></script>
<script type="text/javascript" src="node_modules/ng-file-upload/dist/ng-file-upload.min.js"></script>
<script type="text/javascript" src="node_modules/ng-file-upload/dist/ng-file-upload-shim.min.js"></script>
<script type="text/javascript" src="main.js"></script>
</html>
Here we have declared our angular app as fileUpload
. We are using controller-as syntax ng-controller="MyCtrl as up"
. Named our form as name="up.upload_form"
.
Next, we have added an input type as a file, which will allow us to select files. On top of it, we have added a few directives provided by ng-file-upload
, explained as followed.
: Shows the type of selection is select. Drag and drop is also available.
: Partern to match, type of file.(eg:”‘image/*’” for images)
: Maximum file size allowed to be uploaded.
We have also added ng-model
as up.file
.
Next, we are displaying the thumbnail of the file selected using the ngf-thumbnail
directive.
Below which we have added error-validation messages to display. Finally, we have the submit button and we are also showing the upload progress.
At the bottom the libraries are loaded and our angular app file main.js
.
main.js
main.js
angular.module('fileUpload', ['ngFileUpload'])
.controller('MyCtrl',['Upload','$window',function(Upload,$window){
var vm = this;
vm.submit = function(){ //function to call on form submit
if (vm.upload_form.file.$valid && vm.file) { //check if from is valid
vm.upload(vm.file); //call upload function
}
}
vm.upload = function (file) {
Upload.upload({
url: 'http://localhost:3000/upload', //webAPI exposed to upload the file
data:{file:file} //pass file as data, should be user ng-model
}).then(function (resp) { //upload function returns a promise
if(resp.data.error_code === 0){ //validate success
$window.alert('Success ' + resp.config.data.file.name + 'uploaded. Response: ');
} else {
$window.alert('an error occured');
}
}, function (resp) { //catch error
console.log('Error status: ' + resp.status);
$window.alert('Error status: ' + resp.status);
}, function (evt) {
console.log(evt);
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
console.log('progress: ' + progressPercentage + '% ' + evt.config.data.file.name);
vm.progress = 'progress: ' + progressPercentage + '% '; // capture upload progress
});
};
}]);
Here we have created our app module and defined our controller.
Inject ngFileUpload
as a dependency into our app. ngFileUpload
provides an Upload
service which we need to inject into our controller. We store the controller instance in vm
.
vm.submit()
is the function called on submit of the form. This function validates the form and in turn calls vm.upload()
.
The Upload
service exposes and upload
method, which accepts the web API url and other data. data
object should contain the file model to be uploaded. Note the key in the data object that holds the file must match the parameter in your multer’s single file instance.
In our example its file at both places.
We are also calculating and storing the file upload progress in vm.progress
variable.
This was it for our client-end implementation, you can run the client app on any localserver or the same express server, see below.
Note. These steps may vary if you are not following the same directory structure, ours is as follows, also this structure is only for demonstration purpose, we don’t recommend following it.
Directory Structure
./
client/
node_modules/
index.html
main.js
server/
node_modules/
app.js
package.json
Steps to run the demo app.
git clone <a href="https://github.com/rahil471/file-upload-with-angularjs-and-nodejs.git" target="_blank">https://github.com/rahil471/file-upload-with-angularjs-and-nodejs.git</a>
npm install
gulp</kbd> OR simply run
node app.js`Thus we have seen how to upload files using NodeJS and AngularJS. File Upload is not as difficult as some people assume it to be. This was just a basic example of File upload to point you towards the right direction. Also to note, we have learnt file uploads on both ends(back-end and front-end) and the tutorial of each can be used independently of one another. Eg. You can use Multer file upload with some other front-end framework of your choice similarly you can use ngFileUpload with some other back-end technology of your choice. There is a lot more to file uploads like multiple files, drag and drop, etc. You can know more about it by following our reference links below.
#angular-js #node-js