File Upload is one of those things that is not as straight forward but gets easier once we get our heads around it. In this tutorial, we will see how we can implement File Upload using Angular 2 and Node.js. We have already covered Angular 1 variation of this topic in one of our previous tutorial.
This tutorial comprises of two parts.
- Back-end with NodeJS/ ExpressJS and Multer
- Browser Application with Angular 2 and ng2-File-Upload
Without further ado, let’s get started.
Table of Contents
ToggleBack-end with NodeJS/ ExpressJS and Multer
The back-end implementation for File Upload with Node.js is more or less similar to the one we did in our post for file upload with Angular 1 and Node. There is only one change and that is we will be enabling CORS on our Node server. So here I’ll just display the code, for a detailed explanation please visit this link.
Let’s start by creating a project directory.
mkdir file-upload-demo
Now, let’s navigate into the working directory and create a directory for our Node.js application.
cd file-upload-demo
mkdir node-app
cd node-app
{
"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"
}
}
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var multer = require('multer');
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:3000");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Credentials", true);
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){
console.log(req.file);
if(err){
res.json({error_code:1,err_desc:err});
return;
}
res.json({error_code:0,err_desc:null});
});
});
app.listen('3001', function(){
console.log('running on 3001...');
});
We are using gulp as a task runner for our Node.js app.
var gulp = require( 'gulp' ),
server = require( 'gulp-develop-server' )
jshint = require('gulp-jshint');
gulp.task('lint', function() {
return gulp.src('app.js')
.pipe(jshint())
.pipe(jshint.reporter('default'));
});
// run server
gulp.task( 'server:start', function() {
server.listen( { path: './app.js' } );
});
// restart server if app.js changed
gulp.task( 'server:restart', function() {
gulp.watch( [ './app.js' ], server.restart );
});
gulp.task('default', ['lint','server:start','server:restart']);
We need to have a directory where our uploaded files will go.
mkdir uploads
Install all dependencies, and also install gulp globally.
npm install
npm install gulp -g
We can start up the server with the below command.
gulp
If everything was setup properly you should be able to see the following output on the console.
Browser Application with Angular 2 and ng2-File-Upload
Since you are here, and looking to implement file-upload in Angular 2, I think it’s safe to assume that you have a basic Angular 2 application ready and that your machine is setup to run Angular 2 application. So I’ll skip the setup part and start with a basic pre-built Angular 2 application. If you still wish to know the basics in Angular 2 you can visit the below articles.
- Build a single page application wth Angular 2
- Angular 2 architecture
We will start by cloning the repository into our working directory (in our case it’s /file-upload-demo).
git clone https://github.com/rahil471/angular2-fast-start.git angular2-app
cd angular2-app
Install all the dependencies.
npm install
We will be using ng2-file-upload library to help us with the File Upload for our Angular 2 application. Let’s install it using npm.
npm install ng2-file-upload --save
Now we need to configure our module loader to recognize and find ng2-file-upload. The configuration would be specific to the module loader you are using. For this tutorial, we are using Systems.js as our module loader, so we will see the configuration for the same.
/**
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
app: 'app',
// angular bundles
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api',
/** Path for ng2-file-upload */
'ng2-file-upload' : 'npm:ng2-file-upload'
/** Path for ng2-file-upload */
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './transpiled-js/main.js',
defaultExtension: 'js'
},
rxjs: {
defaultExtension: 'js'
},
'angular-in-memory-web-api': {
main: './index.js',
defaultExtension: 'js'
},
/** Configuration for ng2-file-upload */
'ng2-file-upload' : {
main: './ng2-file-upload.js',
defaultExtension: 'js'
}
/** Configuration for ng2-file-upload */
}
});
})(this);
The ng2-file-upload module provides us with a few directives for achieving file upload, we need to import them and add them to declaration of our AppModule before we can use them.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FileSelectDirective, FileDropDirective } from 'ng2-file-upload';
import { AppComponent } from './app.component';
@NgModule({
imports: [BrowserModule],
exports: [],
declarations: [AppComponent, FileSelectDirective], /** The FileSelectDirective is what we will require*/
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
We are importing and adding FileSelectDirective to the declarations, if we also want to implement the drag and drop feature we will have to add the FileDropDirective .
Basic Usage
In our app.component.ts we will import the FileUploader class and create an uploader object of type FileUploader.
import { Component } from '@angular/core';
import { FileUploader } from 'ng2-file-upload';
@Component({
selector: 'my-app',
template: `
....
.....
`
})
export class AppComponent {
public uploader:FileUploader = new FileUploader({url:'http://localhost:3001/upload'});
}
We are passing the upload URL of our Node.js application which we created earlier in the tutorial in the argument object of FileUploader.
To make this work in our template we will add a ng2FileSelect directive to an html input of type file, we will also set the [uploader] property to our uploader object.
For a single file, the input should look like below
<div class="form-group">
<label for="single">single</label>
<input type="file" class="form-control" name="single" ng2FileSelect [uploader]="uploader" />
</div>
To enable selection of multiple files we only need to add the multiple attribute of HTML5.
The uploader object stores all the selected files in a queue. To upload all the file at once we can call uploader.uploadAll() function or we can call the upload() function availaible on each item of the queue.
<button type="button" class="btn btn-success btn-s"
(click)="uploader.uploadAll()" [disabled]="!uploader.getNotUploadedItems().length">
<span class="glyphicon glyphicon-upload"></span> Upload all
</button><br />
This much in your template should be enough to get the basic thing going, but it’s not that presentable, so we will add a few more elements and try to make it more interactive. We are using bootstrap for styling, you are free to use a framework of your choice.
The complete template our app.component.ts would look like this.
import { Component } from '@angular/core';
import { FileUploader } from 'ng2-file-upload';
@Component({
selector: 'my-app',
template: `<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a>File Upload</a></li>
</ul>
</div>
</div>
</div>
</nav>
<div class="container">
<div class="row">
<div class="col-md-4">
<form>
<div class="form-group">
<label for="multiple">Multiple</label>
<input type="file" class="form-control" name="multiple" ng2FileSelect [uploader]="uploader" multiple />
</div>
<div class="form-group">
<label for="single">single</label>
<input type="file" class="form-control" name="single" ng2FileSelect [uploader]="uploader" />
</div>
</form>
</div>
<div class="col-md-8">
<h3>File Upload with Angular 2 and Node</h3>
Queue length: {{ uploader?.queue?.length }}
<table class="table">
<thead>
<tr>
<th width="50%">Name</th>
<th>Size</th>
<th>Progress</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of uploader.queue">
<td><strong>{{ item.file.name }}</strong></td>
<td nowrap>{{ item.file.size/1024/1024 | number:'.2' }} MB</td>
<td>
<div class="progress" style="margin-bottom: 0;">
<div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': item.progress + '%' }"></div>
</div>
</td>
<td class="text-center">
<span *ngIf="item.isSuccess"><i class="glyphicon glyphicon-ok"></i></span>
<span *ngIf="item.isCancel"><i class="glyphicon glyphicon-ban-circle"></i></span>
<span *ngIf="item.isError"><i class="glyphicon glyphicon-remove"></i></span>
</td>
<td nowrap>
<button type="button" class="btn btn-success btn-xs"
(click)="item.upload()" [disabled]="item.isReady || item.isUploading || item.isSuccess">
<span class="glyphicon glyphicon-upload"></span> Upload
</button>
<button type="button" class="btn btn-warning btn-xs"
(click)="item.cancel()" [disabled]="!item.isUploading">
<span class="glyphicon glyphicon-ban-circle"></span> Cancel
</button>
<button type="button" class="btn btn-danger btn-xs"
(click)="item.remove()">
<span class="glyphicon glyphicon-trash"></span> Remove
</button>
</td>
</tr>
</tbody>
</table>
<div>
<div>
Queue progress:
<div class="progress" style="">
<div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': uploader.progress + '%' }"></div>
</div>
</div>
<button type="button" class="btn btn-success btn-s"
(click)="uploader.uploadAll()" [disabled]="!uploader.getNotUploadedItems().length">
<span class="glyphicon glyphicon-upload"></span> Upload all
</button>
<button type="button" class="btn btn-warning btn-s"
(click)="uploader.cancelAll()" [disabled]="!uploader.isUploading">
<span class="glyphicon glyphicon-ban-circle"></span> Cancel all
</button>
<button type="button" class="btn btn-danger btn-s"
(click)="uploader.clearQueue()" [disabled]="!uploader.queue.length">
<span class="glyphicon glyphicon-trash"></span> Remove all
</button>
</div>
</div>
</div>
</div>`
})
export class AppComponent {
public uploader:FileUploader = new FileUploader({url:'http://localhost:3001/upload'});
}
As I said earlier each file is stored in a queue. Hence, we are repeating through the queue using the ngFor directive. Each item has the following important properties.
- file- File object which contains details related to respective file, including name, size and more.
- progress- Progress of the item beign uploaded in percentage.
- upload()- Method to upload the file.
- cancel()- Cancels and ongoing upoad.
- remove()- Removes item from queue.
And there are a few more properties. Have a closer look at the template to understand them. Apart from the properties and methods on each item we also have methods that operate on the entire queue. For eg: uploadAll(), cancelAll(), removeAll().
Also, please note when we are uploading multiple files together, it does not send all files at once, instead the ng2-file-uploader calls the upload API multiple times depending on the number of items, uploading one at a time.
To start the application run the below command.
npm start
If you’ve followed along properly you should see the following screen on your browser running at localhost:3000.
Considering you have the Node.js app running, you should be able to upload files.
Quick Setup
We know this has been a long tutorial and there are chances you might have slipped at one or two places. No worries, you can quickly get the demo running by following the below steps.
- git clone https://github.com/rahil471/File-upload-Angular2-Nodejs.git file-upload
- Navigate into the node app cd file-upload/node-app
- Install Dependencies npm install
- Install gulp globally npm install gulp -g
- To start the node server gulp
- Open a new terminal window.
- Navigate into /angular2-app/
- Install all dependencies npm install
- In some cases you might have to isntall lite-server globally npm i lite-server -g
- Run the Angular 2 app using npm start
Conclusion
File Upload is one of the most common requirements for any web-application and sometimes it can become a hiccup, but not after this tutorial. In this tutorial, we learned how we can implement File Upload using Node.js and Angular 2 with ease.
More Angular 2 Stuff
- Angular 2 Official Documentation
- Learn Angular 2 From our Free Video Course on YouTube
- Learn Angular 2 by building 12 apps
- Angular 2 by Istvan Novak