Tutorial: Node.js/Express RestAPIs server – Angular 10 Upload/Download Files – Multer + Bootstrap
In the tutorial, we show how to upload files, download files from Angular 10 Client to Node.js RestAPIs server using Multer middleware.
Related posts:
– NodeJS/Express – Upload/Download MultipartFiles/Images – Multer + JQuery Ajax + Bootstrap
– Node.js RestAPIs – Angular 10 HttpClient – Get/Post/Put/Delete requests + Bootstrap 4
Technologies
- Angular 10
- RxJS 6
- Nodejs – v8.11.3
Overview
We create 2 projects: {Node.js, Angular}
Node.js RestAPIs
– Node.js project exposes RestAPIs to upload/download files:
- router.post(‘/api/file/upload’, upload.single(“file”), fileWorker.uploadFile);
- router.get(‘/api/file/all’, fileWorker.listUrlFiles);
- router.get(‘/api/file/:filename’, fileWorker.downloadFile);
Configure cross-origin
for Angular-Client which running at port: 4200
.
const cors = require('cors')
const corsOptions = {
origin: 'http://localhost:4200',
optionsSuccessStatus: 200
}
app.use(cors(corsOptions))
Angular 10 Client
Project structure:
upload-file.service
provides methods: push File to Storage and get Files.list-upload.component
gets and displays list of Files.form-upload.component
helps upload File.details-upload.component
is detail for each item in list of Files.
Practice
Node.js RestAPIs
Setting up NodeJS/Express project
Create a folder Node.js-UploadFiles:
mkdir Node.js-UploadFiles
cd Node.js-UploadFiles
Then init NodeJS project:
>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install ` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (node.js-uploadfiles)
version: (1.0.0)
description: Node.js RestAPIs to Upload Files
entry point: (index.js) server.js
test command:
git repository:
keywords: Node.js,Express,Multer,Upload-Files
author: grokonez.com
license: (ISC)
About to write to C:\nodejs\Node.js-UploadFiles\package.json:
{
"name": "node.js-uploadfiles",
"version": "1.0.0",
"description": "Node.js RestAPIs to Upload Files",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"Node.js",
"Express",
"Multer",
"Upload-Files"
],
"author": "grokonez.com",
"license": "ISC"
}
Is this ok? (yes) yes
We need express
, multer
and cors
modules.
– Express is one of the most popular web frameworks for NodeJs which is built on top of Node.js http module, and adds support for routing, middleware, view system etc.
– Multer is a node.js middleware for handling multipart/form-data , which is primarily used for uploading files.
– Cors: a mechanism that uses HTTP headers to tell a browser to let a web application running at one origin (domain) have permission to access selected resources from a server at a different origin.
-> Install Express, Multer & Cors:
npm install express multer cors --save
-> Check package.json file:
{
"name": "node.js-uploadfiles",
"version": "1.0.0",
"description": "Node.js RestAPIs to Upload Files",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"Node.js",
"Express",
"Multer",
"Upload-Files"
],
"author": "grokonez.com",
"license": "ISC",
"dependencies": {
"cors": "^2.8.4",
"express": "^4.16.3",
"multer": "^1.3.1"
}
}
Config Multer Upload
– Create ./app/config/multer.config.js file:
const multer = require('multer');
var storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, __basedir + '/uploads/')
},
filename: (req, file, cb) => {
cb(null, file.originalname)
}
});
var upload = multer({storage: storage});
module.exports = upload;
Express Routers
– Create ./app/routers/file.router.js file:
let express = require('express');
let router = express.Router();
let upload = require('../config/multer.config.js');
let fileWorker = require('../controllers/file.controller.js');
router.post('/api/file/upload', upload.single("file"), fileWorker.uploadFile);
router.get('/api/file/all', fileWorker.listUrlFiles);
router.get('/api/file/:filename', fileWorker.downloadFile);
module.exports = router;
File Controllers
– Create ./app/controllers/file.controller.js file
const uploadFolder = __basedir + '/uploads/';
const fs = require('fs');
exports.uploadFile = (req, res) => {
res.send('File uploaded successfully! -> filename = ' + req.file.filename);
}
exports.listUrlFiles = (req, res) => {
fs.readdir(uploadFolder, (err, files) => {
for (let i = 0; i < files.length; ++i) {
files[i] = "http://localhost:8080/api/file/" + files[i];
}
res.send(files);
})
}
exports.downloadFile = (req, res) => {
let filename = req.params.filename;
res.download(uploadFolder + filename);
}
Server.js
Implement ./server.js file:
var express = require('express');
var app = express();
const cors = require('cors')
const corsOptions = {
origin: 'http://localhost:4200',
optionsSuccessStatus: 200
}
app.use(cors(corsOptions));
global.__basedir = __dirname;
let router = require('./app/routers/file.router.js');
app.use('/', router);
// Create a Server
let server = app.listen(8080, () => {
let host = server.address().address
let port = server.address().port
console.log("App listening at http://%s:%s", host, port);
})
Angular 10 Client
Generate Service & Components
Run commands below:
ng g s upload/UploadFile
ng g c upload/FormUpload
ng g c upload/ListUpload
ng g c upload/DetailsUpload
On each Component selector, delete app-
prefix, then change tslint.json rules – "component-selector"
to false.
App Module
app.module.ts
->
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { DetailsUploadComponent } from './upload/details-upload/details-upload.component';
import { FormUploadComponent } from './upload/form-upload/form-upload.component';
import { ListUploadComponent } from './upload/list-upload/list-upload.component';
@NgModule({
declarations: [
AppComponent,
DetailsUploadComponent,
FormUploadComponent,
ListUploadComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Upload File Service
upload/upload-file.service.ts
->
import { Injectable } from '@angular/core';
import { HttpClient, HttpEvent, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class UploadFileService {
constructor(private http: HttpClient) { }
pushFileToStorage(file: File): Observable> {
const formdata: FormData = new FormData();
formdata.append('file', file);
const req = new HttpRequest('POST', '/post', formdata, {
reportProgress: true,
responseType: 'text'
});
return this.http.request(req);
}
getFiles(): Observable {
return this.http.get('/getallfiles');
}
}
Component for getting List of Files
upload/list-upload.component.ts
->
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { UploadFileService } from '../upload-file.service';
@Component({
selector: 'list-upload',
templateUrl: './list-upload.component.html',
styleUrls: ['./list-upload.component.css']
})
export class ListUploadComponent implements OnInit {
showFile = false;
fileUploads: Observable;
constructor(private uploadService: UploadFileService) { }
ngOnInit() {
}
showFiles(enable: boolean) {
this.showFile = enable;
if (enable) {
this.fileUploads = this.uploadService.getFiles();
}
}
}
upload/list-upload.component.html
->
<button class="button btn-info" *ngIf='showFile' (click)='showFiles(false)'>Hide Files</button>
<button class="button btn-info" *ngIf='!showFile' (click)='showFiles(true)'>Show Files</button>
<div [hidden]="!showFile">
<div class="panel panel-primary">
<div class="panel-heading">List of Files</div>
<div *ngFor="let file of fileUploads | async">
<div class="panel-body">
<details-upload [fileUpload]='file'></details-upload>
</div>
</div>
</div>
</div>
upload/details-upload.component.ts
->
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'details-upload',
templateUrl: './details-upload.component.html',
styleUrls: ['./details-upload.component.css']
})
export class DetailsUploadComponent implements OnInit {
@Input() fileUpload: string;
constructor() { }
ngOnInit() {
}
}
upload/details-upload.component.html
->
<a href="{{fileUpload}}">{{fileUpload}}</a>
Component for uploading File
upload/form-upload.component.ts
import { Component, OnInit } from '@angular/core';
import { UploadFileService } from '../upload-file.service';
import { HttpResponse, HttpEventType } from '@angular/common/http';
@Component({
selector: 'form-upload',
templateUrl: './form-upload.component.html',
styleUrls: ['./form-upload.component.css']
})
export class FormUploadComponent implements OnInit {
selectedFiles: FileList;
currentFileUpload: File;
progress: { percentage: number } = { percentage: 0 };
constructor(private uploadService: UploadFileService) { }
ngOnInit() {
}
selectFile(event) {
this.selectedFiles = event.target.files;
}
upload() {
this.progress.percentage = 0;
this.currentFileUpload = this.selectedFiles.item(0);
this.uploadService.pushFileToStorage(this.currentFileUpload).subscribe(event => {
if (event.type === HttpEventType.UploadProgress) {
this.progress.percentage = Math.round(100 * event.loaded / event.total);
} else if (event instanceof HttpResponse) {
console.log('File is completely uploaded!');
}
});
this.selectedFiles = undefined;
}
}
upload/form-upload.component.html
->
<div *ngIf="currentFileUpload" class="progress">
<div class="progress-bar progress-bar-info progress-bar-striped" role="progressbar" attr.aria-valuenow="{{progress.percentage}}"
aria-valuemin="0" aria-valuemax="100" [ngStyle]="{width:progress.percentage+'%'}">
{{progress.percentage}}%</div>
</div>
<label class="btn btn-default">
<input type="file" (change)="selectFile($event)">
</label>
<button class="btn btn-success" [disabled]="!selectedFiles" (click)="upload()">Upload</button>
App Component
app.component.ts
->
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'JavaSampleApproach';
description = 'Angular-SpringBoot Demo';
}
app.component.html
->
<div class="container" style="width:400px">
<div style="color: blue; margin-bottom: 20px">
<h1>{{title}}</h1>
<h3>{{description}}</h3>
</div>
<form-upload></form-upload>
<br/>
<br/>
<list-upload></list-upload>
</div>
Check Results
Run Node.js Server (using npm start
) and Angular 10 Client App (using ng serve
)
Then open Browser with url http://localhost:4200/
.
-> Upload files and show list of Files:
-> Inside ‘Node.js-UploadFiles’ project folder, open ‘uploads’ folder:
-> Click to the links to download files: