Angular 13 Express File Upload Tutorial with Reactive Forms
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.
Multer is a Node js middleware, and it helps in uploading the files on the server. Multer makes file uploading easy by adding a body object or a file object to the request object.
This NPM module is quite popular and has been downloaded 618,911 times at the time of creating this tutorial exclusively for file uploading purposes.
Tutorial Objective
We will cover the following topics in this Angular image upload tutorial:
- Setting up MEAN Stack backend.
- Building REST APIs with Express.js for managing file upload.
- Working with Angular Reactive Forms and FormData object.
- Using Multer to store and upload the image files in the Node server.
- Set File Upload Limit using Multer NPM module.
- File Upload with Progress Bar using HttpEvent & HttpEventType API.
- Setting up an Angular app.
- Creating image upload preview in Angular.
- Upload specific image type on node server using Multer MIME-type validation
Table of contents
Install & Set up Angular Project
Run following command to install basic Angular project:
ng new mean-stack-file-upload
Get inside the project folder:
cd mean-stack-file-upload
In order to remove strict type warnings or error make sure to set “strict”: false under compilerOptions property in tsconfig.json file.
We will also install Bootstrap 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.scss"
]
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
Enable Angular Routing
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 { 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>
Set up Node Server
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
Set up Mongoose Schema
In the next step, we will declare the Mongoose Schema for our Angular 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)
Build Express Routes for File Uploading using Multer
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 takesreq, 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, in the package.json file, set “main”: “server.js” name.
Final Node Server Configuration
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');
// Routes to Handle Request
const userRoute = require('../backend/routes/user.route')
// MongoDB Setup
mongoose
.connect('mongodb://127.0.0.1:27017/mydatabase')
.then((x) => {
console.log(`Connected to Mongo! Database name: "${x.connections[0].name}"`)
})
.catch((err) => {
console.error('Error connecting to mongo', err.reason)
})
// 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)
})
We used the express.static()
method. This method is essential and makes a public
folder to publicly available. So when we access files from Angular’s frontend, then we can access these files easily.
Start the MEAN Stack Server
`cd backend`
to enter into the backend folder`nodemon server`
to start the nodemon server`mongod`
to start the mongoDB shell
You can check out uploaded data on the following URL: http://localhost:4000/api
Create Angular File Uploading Service
In this step, we will create an Angular 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(() => {
return 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.
Angular 13 File Upload System with Reactive Forms
In this segment, we will learn to create an Angular 13 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.scss']
})
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
Show User Data List
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.scss']
})
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>
Now, your basic Angular project is ready to be served, run the below command to start the Angular app.
ng serve --open
Conclusion
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.
Make sure to download the sample code from