We will create a basic Angular app and set up a Node.js backend using Node, Express.js, and MongoDB. Then, we will take the help of Multer NPM module to upload and store the files in Node server.
Run following command to install basic Angular project:
ng new mean-stack-file-upload
# ? Would you like to add Angular routing? Yes
# ? Which stylesheet format would you like to use? CSS
Get inside the project folder:
cd mean-stack-file-upload
We will also install Bootstrap 4 by running the following command.
npm install bootstrap
Go to angular.json
file and inject the bootstrap style sheet inside the styles array like given below.
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"src/styles.css"
]
Run given below commands from your terminal to create Angular components for managing file uploading task in a MEAN stack app.
ng g c create-user
ng g c users-list
Now, your basic Angular project is ready to be served, run the below command to start the Angular app.
ng serve --open
Let’s enable routing in our Angular app, go to app-routing.module.ts
file and add the following code inside of it.
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';
import { CreateUserComponent } from './create-user/create-user.component';
import { UsersListComponent } from './users-list/users-list.component';
const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: 'add-user' },
{ path: 'add-user', component: CreateUserComponent },
{ path: 'users-list', component: UsersListComponent }
]
@NgModule({
declarations: [],
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Now, visit app.component.html
file and add the following code to enable routing service in our Angular file uploading demo app.
<ul>
<li>
<a routerLinkActive="active" routerLink="/add-user">Create User</a>
</li>
<li>
<a routerLinkActive="active" routerLink="/users-list">Users</a>
</li>
</ul>
<router-outlet></router-outlet>
We will set up a separate node server for managing image uploading in our Angular application. Create a new folder in the root of our Angular application, name it backend.
Run the following command from the root of your Angular app to generate backend folder:
mkdir backend && cd backend
We will be using separate package.json
file to manage our node server.
npm init
Install required dependencies to build node and express file uploading server:
npm install body-parser cors express mongoose multer --save
Then, install nodemon NPM module, it automatically restarts the node application when it detects the change in the server files.
npm install nodemon --save-dev
Next, we will set up a mongoDB database connection. In mongoDB NoSQL database, we will store our files or images.
Create a folder in backend folder and name it database also create a file and name it db.js
. Here we will define mongoDB database configuration.
Go to backend/database/db.js
file and place the following code in it.
module.exports = {
db: 'mongodb://localhost:27017/meanfileupload'
}
Set up Mongoose Schema
In the next step, we will declare the Mongoose Schema for our Angular 8 MEAN stack file uploading tutorial.
Create a folder name it models
inside the backend folder. Then creates a file and name it User.js
and include the following code in it.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let userSchema = new Schema({
_id: mongoose.Schema.Types.ObjectId,
name: {
type: String
},
avatar: {
type: String
},
}, {
collection: 'users'
})
module.exports = mongoose.model('User', userSchema)
Now we will build Express REST API routes
for file uploading using Multer. Create a new folder inside backend folder and name it routes, inside this folder also create a new file and name it user.route.js
.
Create a new folder by the name of public
in the backend folder. When a user makes the HTTP POST request via Express.js
route from Angular service then in this folder an image will be stored.
Go to backend/routes/user.route.js
file and add the following code.
let express = require('express'),
multer = require('multer'),
mongoose = require('mongoose'),
router = express.Router();
// Multer File upload settings
const DIR = './public/';
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, DIR);
},
filename: (req, file, cb) => {
const fileName = file.originalname.toLowerCase().split(' ').join('-');
cb(null, fileName)
}
});
// Multer Mime Type Validation
var upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: (req, file, cb) => {
if (file.mimetype == "image/png" || file.mimetype == "image/jpg" || file.mimetype == "image/jpeg") {
cb(null, true);
} else {
cb(null, false);
return cb(new Error('Only .png, .jpg and .jpeg format allowed!'));
}
}
});
// User model
let User = require('../models/User');
// POST User
router.post('/create-user', upload.single('avatar'), (req, res, next) => {
const url = req.protocol + '://' + req.get('host')
const user = new User({
_id: new mongoose.Types.ObjectId(),
name: req.body.name,
avatar: url + '/public/' + req.file.filename
});
user.save().then(result => {
console.log(result);
res.status(201).json({
message: "User registered successfully!",
userCreated: {
_id: result._id,
name: result.name,
avatar: result.avatar
}
})
}).catch(err => {
console.log(err),
res.status(500).json({
error: err
});
})
})
// GET All User
router.get("/", (req, res, next) => {
User.find().then(data => {
res.status(200).json({
message: "Users retrieved successfully!",
users: data
});
});
});
// GET User
router.get("/:id", (req, res, next) => {
User.findById(req.params.id).then(data => {
if (data) {
res.status(200).json(post);
} else {
res.status(404).json({
message: "User not found!"
});
}
});
});
module.exports = router;
We imported the express, multer, and mongoose NPM modules to make the REST APIs routes.
Declare the Dir variable and define the public
directory path, where all the images or files will be stored.
We are using multer disktorage
middleware. In this method, we used destination and filename methods. Multer’s destination method stores files in the public folder. The filename method takes req, file, cb
arguments, and helps in defining the name of the file.
Setting up file upload limit and file type validation is easy by using Multer NPM Module. In the above example, we used limits key to defining fileSize
, and file upload limit is upto 5mb.
Multer’s fileFilter
method allows MIME-type validation, we implemented specific file type validation in which we can upload images with particular file types such as .png, .jpg, and .jpeg format.
We created the express route by the name of /create-user
, this middleware takes req, res, next arguments. We can define the Multer’s upload object directly with the express route. Whenever this API is being called, then the file will be saved in the public directory.
Next, we will create server.js file in the backend folder’s root. Here we will define the server configurations such as mongoDB database, Express routes, Express server setup, Express Static Path, Server PORT and Error handling methods:
Go to backend/server.js
file and add the following code inside of it.
let express = require('express'),
mongoose = require('mongoose'),
cors = require('cors'),
bodyParser = require('body-parser'),
dbConfig = require('./database/db');
// Routes to Handle Request
const userRoute = require('../backend/routes/user.route')
// MongoDB Setup
mongoose.Promise = global.Promise;
mongoose.connect(dbConfig.db, {
useNewUrlParser: true
}).then(() => {
console.log('Database sucessfully connected')
},
error => {
console.log('Database could not be connected: ' + error)
}
)
// Setup Express.js
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(cors());
// Make "public" Folder Publicly Available
app.use('/public', express.static('public'));
// API Route
app.use('/api', userRoute)
// Error favicon.ico
app.get('/favicon.ico', (req, res) => res.status(204));
const port = process.env.PORT || 4000;
const server = app.listen(port, () => {
console.log('Connected to port ' + port)
})
// Error
app.use((req, res, next) => {
// Error goes via `next()` method
setImmediate(() => {
next(new Error('Something went wrong'));
});
});
app.use(function (err, req, res, next) {
console.error(err.message);
if (!err.statusCode) err.statusCode = 500;
res.status(err.statusCode).send(err.message);
});
Note : We used the
express.static()
method. This method is essential and makes apublic
folder to publicly available. So when we access files from Angular’s frontend, then we can access these files easily.
cd backend
to enter into the backend foldernodemon server
to start the nodemon servermongod
to start the mongoDB shellIn this step, we will create an Angular 8 service to handle node server REST APIs for our file upload tutorial.
But before that create a folder and name it shared inside Angular’s src/app folder.
Inside the src/app/shared
folder create user.ts class, and define the following code inside of it.
export class User {
id: string;
name: string;
avatar: string;
}
Next, we will import the HttpClientModule service in app.module.ts
file:
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [...],
imports: [
HttpClientModule
],
bootstrap: [...]
})
export class AppModule { }
Then, go to src/app/shared
folder and create file-upload.service.ts file, and place the given below code in it.
import { Injectable } from '@angular/core';
import { User } from './user';
import { Observable, throwError } from 'rxjs';
import { HttpHeaders, HttpErrorResponse, HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class FileUploadService {
baseURL = "http://localhost:4000/api";
headers = new HttpHeaders().set('Content-Type', 'application/json');
constructor(private http: HttpClient) { }
// Get Users
getUsers() {
return this.http.get(this.baseURL)
}
// Create User
addUser(name: string, profileImage: File): Observable<any> {
var formData: any = new FormData();
formData.append("name", name);
formData.append("avatar", profileImage);
return this.http.post<User>(`${this.baseURL}/create-user`, formData, {
reportProgress: true,
observe: 'events'
})
}
// Error handling
errorMgmt(error: HttpErrorResponse) {
let errorMessage = '';
if (error.error instanceof ErrorEvent) {
// Get client-side error
errorMessage = error.error.message;
} else {
// Get server-side error
errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
}
console.log(errorMessage);
return throwError(errorMessage);
}
}
We created the Angular service for the file uploading task. In this service file, we defined the methods such as getUsers
to retrieve the user’s data from the mongoDB database and addUser
method to upload the user data such as name and profile image to the mongoDB database. In order to use this service we have to import this service and inject inside the component’s constructor method in Angular’s component.
To upload the file or image in the mongoDB database via node server, we are using the FormData object. The FormData interface provides a way to easily construct a set of key/value pairs describing form fields and their values. We passed the name and profileImage as an argument. Then we declared the FormData object and created a formData instance from it. After that, we used the formData.append() method to inject the values retrieved from the Reactive form.
Next, we are using the Http POST method to send the user data to the server. We passed the two arguments in the POST method; first, we passed the REST API route, and the second argument is the fromData created with FormData object. We also defined reportProgress: true and observe: ‘events’ value because we want to track the Http request’s progress.
In this segment, we will learn to create an Angular 8 file uploading system with Reactive Forms.
Go to app.module.ts
file and import the ReactiveFormsModule service.
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [...],
imports: [
ReactiveFormsModule
],
bootstrap: [...]
})
export class AppModule { }
Go to src/app/create-user.component.ts
file and add the following code.
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from "@angular/forms";
import { FileUploadService } from "../shared/file-upload.service";
import { HttpEvent, HttpEventType } from '@angular/common/http';
import { Router } from '@angular/router';
@Component({
selector: 'app-create-user',
templateUrl: './create-user.component.html',
styleUrls: ['./create-user.component.css']
})
export class CreateUserComponent implements OnInit {
preview: string;
form: FormGroup;
percentDone: any = 0;
users = [];
constructor(
public fb: FormBuilder,
public router: Router,
public fileUploadService: FileUploadService
) {
// Reactive Form
this.form = this.fb.group({
name: [''],
avatar: [null]
})
}
ngOnInit() { }
// Image Preview
uploadFile(event) {
const file = (event.target as HTMLInputElement).files[0];
this.form.patchValue({
avatar: file
});
this.form.get('avatar').updateValueAndValidity()
// File Preview
const reader = new FileReader();
reader.onload = () => {
this.preview = reader.result as string;
}
reader.readAsDataURL(file)
}
submitForm() {
this.fileUploadService.addUser(
this.form.value.name,
this.form.value.avatar
).subscribe((event: HttpEvent<any>) => {
switch (event.type) {
case HttpEventType.Sent:
console.log('Request has been made!');
break;
case HttpEventType.ResponseHeader:
console.log('Response header has been received!');
break;
case HttpEventType.UploadProgress:
this.percentDone = Math.round(event.loaded / event.total * 100);
console.log(`Uploaded! ${this.percentDone}%`);
break;
case HttpEventType.Response:
console.log('User successfully created!', event.body);
this.percentDone = false;
this.router.navigate(['users-list'])
}
})
}
}
Next, go to src/app/create-user.component.html
file and add the following code.
<form [formGroup]="form" (ngSubmit)="submitForm()">
<!-- Progress Bar -->
<div class="progress form-group" *ngIf="fileUploadService.percentDone">
<div class="progress-bar progress-bar-striped bg-success" role="progressbar"
[style.width.%]="fileUploadService.percentDone">
</div>
</div>
<!-- Image Preview -->
<div class="form-group">
<div class="preview" *ngIf="preview && preview !== null">
<img [src]="preview" [alt]="form.value.name">
</div>
</div>
<!-- File Input -->
<div class="form-group">
<input type="file" (change)="uploadFile($event)">
</div>
<!-- Name -->
<div class="form-group input-group-lg">
<input class="form-control" placeholder="Name" formControlName="name">
</div>
<!-- Submit -->
<div class="form-group">
<button class="btn btn-danger btn-block btn-lg">Create User</button>
</div>
</form>
We used created the basic form using Bootstrap 4 UI components.
We are using Reactive Forms to manage the data.
To show image preview in Angular, we declared the uploadFile method and using the FileReader method to create the reader instance. The reader instance will use the readAsDataURL method and convert the base64 image to show the image preview. You can check out this detailed article on Angular image preview with Reactive Forms.
Next, access the addUser method from Angular service. This method will take name and avatar values to store the data on the MongoDB database. When we subscribe to this method, then it will also track the File or data upload with progress bar using HttpEvent and HttpEventType services
Next, we will show user uploaded data on Angular’s frontend, go to users-list/users-list.component.ts
file and add the following code inside of it.
import { Component, OnInit } from '@angular/core';
import { FileUploadService } from "../shared/file-upload.service";
@Component({
selector: 'app-users-list',
templateUrl: './users-list.component.html',
styleUrls: ['./users-list.component.css']
})
export class UsersListComponent implements OnInit {
Users: any = [];
constructor(public fileUploadService: FileUploadService) {
this.getUsers();
}
ngOnInit() { }
getUsers() {
this.fileUploadService.getUsers().subscribe((res) => {
this.Users = res['users'];
})
}
}
Then, go to users-list/users-list.component.html
file and include the given below
code inside of it.
<div class="container user-table">
<!-- No data message -->
<div class="alert alert-success text-center" role="alert" *ngIf="Users.length <= 0">
No Users added yet!
</div>
<ul class="list-unstyled">
<li class="media" *ngFor="let user of Users; let i = index">
<img [src]="user.avatar" class="mr-3" [alt]="user.name">
<div class="media-body">
<h5 class="mt-0 mb-1">{{user.name}}</h5>
{{user._id}}
</div>
</li>
</ul>
</div>
Finally, we have completed Angular 8 and Express JS File Upload with Reactive Forms tutorial. In this tutorial, we learnt to upload files from Angular app to mongoDB database using node and express server. We learned to show image preview and making image or file upload progress bar using HttpEvent progress API service. We explored about Multer NPM module, and it’s middleware. I hope you enjoyed this article, please consider it sharing with others.
Let’s share it!
#angular #javascript #reactjs