File Upload with AngularJS and NodeJS

File Upload with AngularJS and NodeJS

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.

Prerequisite

This article assumes you have already worked with AngularJS and Node expressjs and have a basic knowledge of them.
 

DEMO  DOWNLOAD

 

Lets begin

We will be dividing this into two sections, server side with node and the client end with AngularJS.

Backend with NodeJS

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" &amp;&amp; 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.
 

Explanation

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.
 

File API

Each uploaded file object contains the following information.

file object
file object

Run Express server

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.
 

Front-end in AngularJS

We will be using ng-file-upload module in AngularJS for facilitating file uploads.
 

Setting Up

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.

  • ngf-select
  • : Shows the type of selection is select. Drag and drop is also available.

  • ngf-pattern
  • : Partern to match, type of file.(eg:”‘image/*'” for images)

  • ngf-max-size
  • : 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
    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 &amp;&amp; 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. ngFileUploadprovides 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.

Running our App

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.

  • Download the code from here OR clone our repository by running git clone https://github.com/rahil471/file-upload-with-angularjs-and-nodejs.git
  • Navigate to the server directory in our app using command-line.
  • Run npm install
  • If you use gulp run gulp OR simply run node app.js
  • Open http://localhost:3000 in your browser

 

Conclusion

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.
 

Reference Links

Like our articles? Get them straight to your inbox!

* indicates required
File Upload with AngularJS and NodeJS
Tagged on:         

54 thoughts on “File Upload with AngularJS and NodeJS

  • January 26, 2016 at 10:27 pm
    Permalink

    Great tutorial.
    I need example how can I view the images in the web after upload the files.
    Thanks.

    Reply
    • January 27, 2016 at 6:18 am
      Permalink

      Thanks,
      Are you referring to the preview of images while uploading OR to display images after they have been uploaded?

      Reply
      • January 27, 2016 at 9:20 am
        Permalink

        After they have been uploaded

        Reply
        • January 28, 2016 at 5:23 am
          Permalink

          You should store the name of the image or the complete URL into your data-store. The path should be accessible via web. They you only have to pass the URL via your API into your client-end.

          Reply
          • January 28, 2016 at 6:48 pm
            Permalink

            You have any example how to do it?

          • January 29, 2016 at 10:04 am
            Permalink

            I stuck at it for a long time please help me?

  • April 17, 2016 at 3:58 am
    Permalink

    Sir if i want to separate images in different folder and also is want that name by their id then what changes are made into thia example

    Reply
  • April 26, 2016 at 11:43 am
    Permalink

    Sir, Thanks for your great tutorial. Is there any way to change the file name in main.js?

    Reply
    • April 27, 2016 at 6:03 am
      Permalink

      Welcome, you would probably want to change the filename in the back-end rather than front-end.

      Reply
  • April 30, 2016 at 3:05 am
    Permalink

    Hey I was able to get the upload working but everytime i upload my picture. It ends up being really blurry. Is there a config i need to set up?

    Reply
    • May 10, 2016 at 7:55 am
      Permalink

      Have you added the static path to your client directory?

      app.use(express.static('../client'));

      Follow the demo for instructions to run the app.

      Reply
  • June 2, 2016 at 8:00 am
    Permalink

    Hi !
    Thank you, that’s a good tuto.

    Reply
  • June 15, 2016 at 2:00 pm
    Permalink

    hi dude can you please tell me how upload two images simultaneously from two separate file boxes from angular and upload it in folder and database using nodejs. mysql as a database

    Reply
    • September 25, 2016 at 9:54 am
      Permalink

      hi arbaz,

      i think you can do this by adding your database call to save the file name in the event filename for multer settings where the request is file is parsed before moving to upload folder.

      Regards,
      SJ

      Reply
  • June 16, 2016 at 5:11 am
    Permalink

    Great tutorial. Can I create a new javascript blob/file object and pass it directly without using form data ? What’s the recommended entry point to add this new object ? It’s always
    going to be the same file and requires no user interaction.
    Thanks

    Reply
    • June 16, 2016 at 2:53 pm
      Permalink

      You are welcome dan, is this really what you want? Because the path of the file may differ from client to client and in some cases the file you are looking for would not even exist leading to unnecessary errors.

      Reply
      • June 16, 2016 at 5:56 pm
        Permalink

        Rahil,

        All the clients are in the same internal private network. The download path will be setup ahead of time and will always be identical for each client workstation, for example http://localhost:3000/uploads/test.txt.
        Each day all test.txt files will be used as inputs for further processing. There is no user interface such as
        selecting files from an html form. Can I create this javascript object at runtime and insert it after
        app.use(bodyParser.json()); and let the rest of the process continue ?
        var file = new File([some variable], “test.txt”, { type:”text/plain”, lastModified:date});
        Thanks

        Reply
        • June 17, 2016 at 4:04 am
          Permalink

          I pasted the above snippet into a javascript syntax checker and it failed. Any ideas ?
          Thanks

          Reply
          • June 20, 2016 at 6:44 am
            Permalink

            It was a typing mistake on my part. We will go with an html form for user submission.
            thanks

  • June 23, 2016 at 12:02 pm
    Permalink

    thank you sir,its great tutorial, i am done with uploading files but my files are uploaded only in C: drive under uploads folder. how can i give path to my server directory? i want them to be uploaded inside my public directory under uploads folder, so that it will be easy to fetch data.

    Reply
  • July 8, 2016 at 12:35 pm
    Permalink

    Hi Bro

    second node app.js cannot see port no why?

    Reply
  • July 17, 2016 at 4:40 pm
    Permalink

    I spent 2 hours trying to figure out how to do this by myself… then I did it in 2 minutes with your tutorial. Thank you!

    Reply
    • July 17, 2016 at 5:03 pm
      Permalink

      Glad it was useful to you and thanks for leaving that positive comment. We are featuring this comment on our facebook page.

      Reply
  • July 24, 2016 at 1:53 pm
    Permalink

    Thanks for that article, you really saved my day!

    Reply
  • August 3, 2016 at 2:24 am
    Permalink

    Thanks for the post Rahil. I worked for me. Just wanted to know if this can be used to upload a csv or xml file .

    Thanks!

    Reply
  • September 21, 2016 at 11:00 am
    Permalink

    Hi i have a error in the demo

    XMLHttpRequest cannot load http://localhost:3000/upload. Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header has a value 'http://localhost' that is not equal to the supplied origin. Origin 'http://localhost:63342' is therefore not allowed access.
    main.js:24 ProgressEvent {isTrusted: true, config: Object, lengthComputable: false, loaded: 0, total: 0…}
    main.js:26 progress: NaN% almogInTheSea.jpg
    main.js:21 Error status: -1
    Reply
    • September 21, 2016 at 12:38 pm
      Permalink

      That’s because you are running the demo on another port.

      You are ruining into a cross origin request issue. This happens when you request something to another host for eg: localhost to localhost:3000

      You either need to move your client and server application to a common host or you need to enable cross origin request on the server. Just do a bit of search on it and you will be set to go.

      This is an example for an express applications.

      app.use(function(req, res, next) {
              res.header("Access-Control-Allow-Origin", "--YOUR--HOST--TO--ALLOW--");
              res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
              next();
          });

      In your case the host seems to be

      http://localhost:63342
      Reply
      • September 21, 2016 at 4:45 pm
        Permalink

        thank you very much for fast answer,
        i try to study alone everything So sometimes I get stuck on small issues

        Reply
          • September 22, 2016 at 8:00 am
            Permalink

            hello again 🙂
            Can you tell me exactly where I change the server location of uploaded images ?

          • September 22, 2016 at 8:16 am
            Permalink

            its ok i found the answer 🙂

  • October 20, 2016 at 9:14 am
    Permalink

    how can i get req.body.file

    Reply
  • December 6, 2016 at 11:50 am
    Permalink

    I am a rookie in these stuffs.
    I have to submit similar SPA for an interview assignment, but I am doubtful if using ngFileUpload is if the right way or legit way as I am directly using it as library, will it make any difference to my assesment?
    Also I need to save this uploaded images in database someway MongoDB or MYSQL, can you tell me how to do that or approach that ?.
    I have to make an Image up loader.

    Thanks

    Reply
    • December 10, 2016 at 2:13 pm
      Permalink

      Using 3rd party open source library is perfectly fine! I would recommend storing paths in database and placing images into that path on the server.

      Reply
  • January 5, 2017 at 3:58 pm
    Permalink

    Dear Rahil,
    I am able to upload the file to my local hard drive but how do I upload the file to JIRA? I am trying to upload the file to the JIRA server using the JIRA api. Please advise.

    Reply
  • January 10, 2017 at 11:15 am
    Permalink

    i need small help .while uploading image i need to collect email and name and after uploading into folder i need to store image path,email and name into database .how can i do this please help me out .i am new to this technology

    Reply
  • January 18, 2017 at 7:20 am
    Permalink

    Hi Rahil,
    Very good your article, clear and reliable.
    Do you know how I could add data to the POST, along with the file, to the server?
    I’ve tried the following, without success:

    Upload.upload({
    url: ‘api/applications’,
    file: vm.file,
    someData: { “key1”: “one”, “key2”, “two” }
    }).then(function (resp) {
    // …….
    }
    If you can advise that, then how could I get this data on the node js side, from “req.body” ?

    Reply
    • January 19, 2017 at 5:53 am
      Permalink

      The data you have passed along with the file would be available within your multer upload function and not above it.

      Reply
  • March 6, 2017 at 11:02 pm
    Permalink

    Hi, I keep getting the 413 response. ANy ideas? (Error: request entity too large) The files I am trying to upload are not very big, some even under 1mb. I played around with the bodyparser settings but no luck so far.

    Reply
    • March 7, 2017 at 2:18 am
      Permalink

      Increasing the body parser limit should do the trick.
      If not then are you using any server as a proxy? If it’s a webserver like nginx or Apache, you will to change settings over there as well

      Reply
      • March 13, 2017 at 10:01 pm
        Permalink

        Thanks for your answer. Now: different problem. req.file is giving me undefined while I get a 200 back. Have been googling for 2 days, no luck :-(. Ideas?

        This is the payload: ——WebKitFormBoundaryfNaA6TQQwD4sBNJL
        Content-Disposition: form-data; name=”file”; filename=”FullSizeRender.jpg”
        Content-Type: image/jpeg

        ——WebKitFormBoundaryfNaA6TQQwD4sBNJL–

        Reply
    • March 22, 2017 at 4:37 pm
      Permalink

      You are welcome!

      Reply
  • April 10, 2017 at 10:31 am
    Permalink

    Hi Dude, how to select multiple images and upload using your coed.

    Reply
  • April 11, 2017 at 1:26 pm
    Permalink

    It is an amazing tutorial! The only one that I see with complete explanation about it! But I have a doubt… In case of the entire settings equal to the tutorial, what would be the directory structure considering the “uploads” directory? For some reason, although it does not display any errors, the image is simply not written to the uploads directory (in my case, it is at the same level as the client and server directories)

    Reply

Leave a Reply