Angular 6 + Node.js + Amazon S3 | Upload Files + Download Files + List Files | using Express RestAPI, Multer, AWS-SDK

angular-6-node-js-amazon-s3-upload-files-download-files-list-files-using-express-restapi-multer-aws-sdk-feature-image

In the tutorial, we show how to use Angular 6 Client to download files/ upload files from Amazon S3 by Node.js RestAPIs server using Multer middleware and AWS-SDK.

Related posts:
Node.js RestAPIs Download File from Amazon S3
Node.js RestAPIs Upload file to Amazon S3
Node.js RestAPIs – Angular 6 HttpClient – Get/Post/Put/Delete requests + Bootstrap 4
Amazon S3 – Upload/download large files to S3 with SpringBoot Amazon S3

Related pages:

Technologies

  • Angular 6
  • Node.js/Express
  • Express
  • Multer
  • AWS-SDK

Overview

We create 2 projects: {Node.js, Angular} ->

Demo

Node.js RestAPIs

Project structure ->

angular-6-nodejs-express-restapis-upload-file-download-file-to-amazon-s3-using-multer-middleware-aws-sdk-nodejs-project

– Node.js use AWS-SDK S3 to download/upload files ->

const s3 = new AWS.S3({
    accessKeyId: env.AWS_ACCESS_KEY,
    secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
	region : env.REGION
});

– S3 APIs to download/upload/list files ->

...
// -> Do upload Files
s3.upload(params, (err, data) => {
	if (err) {
		res.status(500).send("Error -> " + err);
	}
	res.send("File uploaded successfully! -> keyname = " + req.file.originalname);
});

...

// -> Do download Files
s3.getObject(params)
	.createReadStream()
		.on('error', function(err){
			res.status(500).json({error:"Error -> " + err});
	}).pipe(res);
	
// -> List all Files 
s3.listObjectsV2(params, (err, data) => {
	if (err) {
		console.log(err, err.stack); // an error occurred
		res.send("error -> "+ err);
	} else {
		var contents = data.Contents;
		contents.forEach(function (content) {
			keys.push(content.Key);
		});
		res.send(keys);
	}
});

– Node.js exposes RestAPIs to download/upload/list files ->

  • router.post('/api/files/upload', upload.single("file"), awsWorker.doUpload);
  • router.get('/api/files/all', awsWorker.listKeyNames);
  • router.get('/api/files/:filename', awsWorker.doDownload);

– 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 Client

Project structure ->

angular-6-nodejs-express-restapis-upload-file-download-file-to-amazon-s3-using-multer-middleware-aws-sdk-angular-6-project

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-nodejs-express-restapis-upload-file-download-file-to-amazon-s3-using-multer-middleware-aws-sdk-angular-6-project-design

Practice

Node.js RestAPIs
Setting up Node.js project

Create a folder NodejsUploadDownloadFileAmazonS3, then init Node.js 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: (nodejsuploaddownloadfileamazons3)
version: (1.0.0)
description: Node.js Express RestAPIs upload/download/list file to/from Amazon S3 using Multer, AWS-SDK
entry point: (index.js) server.js
test command:
git repository:
keywords: Nodejs, Express, Amazon S3, RestAPI, Upload File, Download File, List File, Multer, Aws SDK
author: ozenero.com
license: (ISC)
About to write to D:\gkz\article\NodejsUploadDownloadFileAmazonS3\package.json:
 
{
  "name": "nodejsuploaddownloadfileamazons3",
  "version": "1.0.0",
  "description": "Node.js Express RestAPIs upload file, download file, list files to Amazon S3 using Multer, AWS-SDK",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "Nodejs",
    "Express",
    "Amazon",
    "S3",
    "RestAPI",
    "UploadFile",
	"DownloadFile",
    "Multer",
    "Aws",
    "SDK"
  ],
  "author": "ozenero.com",
  "license": "ISC"
}
 
 
Is this ok? (yes) yes

Install Express, Multer, Cors, AWS-SDK ->

npm install express multer cors aws-sdk --save

-> package.json file:

{
  "name": "nodejsuploaddownloadfileamazons3",
  "version": "1.0.0",
  "description": "Nodejs Express RestAPIs uploads file/downloads file from Amazon S3",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "Nodejs",
    "Express",
    "RestAPI",
    "DownloadFile",
    "Amazon",
    "S3",
    "AWS-SDK",
    "Multer",
    "Cors"
  ],
  "author": "ozenero.com",
  "license": "ISC",
  "dependencies": {
    "aws-sdk": "^2.302.0",
    "cors": "^2.8.4",
    "express": "^4.16.3",
    "multer": "^1.3.1"
  }
}
Multer Config

– Create ./app/config/multer.config.js file:

const multer = require('multer');
 
var storage = multer.memoryStorage()
var upload = multer({storage: storage});
 
module.exports = upload;
AWS S3 Config

– Create a file ./app/config/s3.env.js ->

const env = {
	AWS_ACCESS_KEY: 'AKIAI7PUDS2W5HMSVQRA', // change to yours
	AWS_SECRET_ACCESS_KEY: '5xmrnGFK7klMGQovhFNssJE96j4NN4F3xFi7wBc1', // change to yours
	REGION : 'us-east-2', // change to yours
	Bucket: 'ozenero-s3-bucket' // change to yours
};
 
module.exports = env;

– Configure S3 Client in a file ./app/config/s3.config.js ->

const AWS = require('aws-sdk');
const env = require('./s3.env.js');
 
const s3 = new AWS.S3({
    accessKeyId: env.AWS_ACCESS_KEY,
    secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
	region : env.REGION
});

 
module.exports = s3;
Express Routers

– Create router in file ./app/routers/s3.router.js ->

let express = require('express');
let router = express.Router();
 
let upload = require('../config/multer.config.js');

const awsWorker = require('../controllers/s3.controller.js');
 
router.post('/api/files/upload', upload.single("file"), awsWorker.doUpload);

router.get('/api/files/all', awsWorker.listKeyNames);

router.get('/api/files/:filename', awsWorker.doDownload);
 
module.exports = router;
Upload/Download/List Files Controller

– Implement S3 Uploader in a controller file ./app/controllers/s3.controller.js ->

const s3 = require('../config/s3.config.js');
const env = require('../config/s3.env.js');
 
exports.doUpload = (req, res) => {
	const params = {
		Bucket: env.Bucket,
		Key: req.file.originalname,
		Body: req.file.buffer
	}
	
	s3.upload(params, (err, data) => {
		if (err) {
			res.status(500).send("Error -> " + err);
		}
		res.send("File uploaded successfully! -> keyname = " + req.file.originalname);
	});
}

exports.listKeyNames = (req, res) => {
	const params = {
		Bucket: env.Bucket
	}

	var keys = [];
	s3.listObjectsV2(params, (err, data) => {
        if (err) {
			console.log(err, err.stack); // an error occurred
			res.send("error -> "+ err);
        } else {
            var contents = data.Contents;
            contents.forEach(function (content) {
                keys.push(content.Key);
			});
			res.send(keys);
		}
	});
}

exports.doDownload = (req, res) => {
	const params = {
		Bucket: env.Bucket,
		Key: req.params.filename
	}

	res.setHeader('Content-Disposition', 'attachment');

	s3.getObject(params)
		.createReadStream()
			.on('error', function(err){
				res.status(500).json({error:"Error -> " + err});
		}).pipe(res);
}
Server.js

server.js ->

const express = require('express');
const app = express();

const cors = require('cors')
const corsOptions = {
  origin: 'http://localhost:4200',
  optionsSuccessStatus: 200
}
app.use(cors(corsOptions))
 
let router = require('./app/routers/s3.router.js');
app.use('/', router);
 
// Create a Server
const server = app.listen(8080, function () {
 
  let host = server.address().address
  let port = server.address().port
 
  console.log("App listening at http://%s:%s", host, port); 
})
Angular 6 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 ->


 

 
List of Files

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 ->

{{fileUpload}}
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 ->

{{progress.percentage}}%
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 = 'ozenero';
  description = 'Angular-Node.js Demo';
}

app.component.html ->

{{title}}

{{description}}



Run & Check Results

Run Node.js Server (using npm start) and Angular 6 Client App (using ng serve)
Then open Browser with url http://localhost:4200/.

-> Upload files and show list of Files:

angular-6-nodejs-express-restapis-upload-file-download-file-to-amazon-s3-using-multer-middleware-aws-sdk-upload-files

-> Amazon S3 Files:

angular-6-nodejs-express-restapis-upload-file-download-file-to-amazon-s3-using-multer-middleware-aws-sdk-amazon-s3-bucket

-> Click to the links to download files:

angular-6-nodejs-express-restapis-upload-file-download-file-to-amazon-s3-using-multer-middleware-aws-sdk-download-files-successfully

SourceCode

NodejsUploadDownloadFileAmazonS3
Angular-6-Upload-Download-Files

37 thoughts on “Angular 6 + Node.js + Amazon S3 | Upload Files + Download Files + List Files | using Express RestAPI, Multer, AWS-SDK”

  1. Excellent work, I have a doubt. Try uploading a 24MB file and the upload does not end, will it have anything to do with memory storage?

  2. Nicee post. I was checking continuously this blog andd I’m impressed!
    Very useful information specially tthe laszt part 🙂 I care for such info
    a lot. I was looking for this partficular information for a long time.Thank you and best
    of luck.

  3. Excellent post. I was checkinbg continuously this blog and I aam impressed!
    Extremely useful information. I ccare for such information a lot.

    I was looking for this certain information for a very long time.Thank youu and
    good luck.

  4. Excellent post. I was checking continuously this bog and I am impressed!
    Extremely useful information. I care for such information a lot.
    I was looking for this certain incormation for a very long time.Thabk you annd giod luck.

Leave a Reply

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