Node.js/Express RestAPIs server – Angular 11 Upload/Download Files

Tutorial: Node.js/Express RestAPIs server – Angular 11 Upload/Download Files – Multer + Bootstrap

In the tutorial, we show how to upload files, download files from Angular 11 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 11 HttpClient – Get/Post/Put/Delete requests + Bootstrap 4

Technologies

  • Angular 11
  • 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-6-upload-multipart-file-nodejs-restapis-nodejs-project-structure

Angular 11 Client

Project structure:

angular-6-upload-multipart-file-nodejs-restapis-angular-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.

angular-6-upload-multipart-file-nodejs-restapis-angular-overview

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: ozenero.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": "ozenero.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": "ozenero.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 11 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 11 Client App (using ng serve)
Then open Browser with url http://localhost:4200/.

-> Upload files and show list of Files:

angular-6-upload-multipart-file-nodejs-restapis-upload-files

-> Inside ‘Node.js-UploadFiles’ project folder, open ‘uploads’ folder:

angular-6-upload-multipart-file-nodejs-restapis-upload-files-result

-> Click to the links to download files:

angular-6-upload-multipart-file-nodejs-restapis-nodejs-download-files

Sourcecode

16 thoughts on “Node.js/Express RestAPIs server – Angular 11 Upload/Download Files”

  1. When I originally commented I clicked the “Notify me when new comments are added” checkbox and now each time a comment is added I get four emails with the same comment. Is there any way you can remove people from that service? Thank you!

  2. Cool blog! Is your theme custom made or did you download it from somewhere?
    A theme like yours with a few simple tweeks would really make my blog
    shine. Please let me know where you got your design. Thanks

  3. Hi, I do believe this is an excellent web site. I stumbledupon it 😉 I may return once again since
    i have book marked it. Money and freedom is the best
    way to change, may you be rich and continue to guide other people.

  4. I know this if off topic but I’m looking into starting my own weblog and was wondering what all is required to get set up?
    I’m assuming having a blog like yours would cost a pretty penny?
    I’m not very web savvy so I’m not 100% positive.
    Any tips or advice would be greatly appreciated.
    Appreciate it

  5. This is very interesting, You’re a very skilled blogger.
    I’ve joined your rss feed and look forward to seeking more of your fantastic post.
    Also, I have shared your web site in my social networks!

  6. Hey there this is somewhat of off topic but I was wanting to know
    if blogs use WYSIWYG editors or if you have to manually code with HTML.
    I’m starting a blog soon but have no coding experience so I wanted to get guidance from
    someone with experience. Any help would be enormously appreciated!

Leave a Reply

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