How To Upload Images In Angular 2 Applications

In this tutorial, I will explain how to upload images in Angular 2. In this demo, I am using Angular 2 as front-end and node.js (with express template) as back-end along with MySQL as database. I am using Angular-cli to create the project. I hope you guys have already created the project using Angular-cli. If not, you can find how to create one.

You can also find both back-end and front-end links here.

Note

To run both projects, you need to install dependency from the package.json. You can simple do that by navigating to the project directory and writing the command "npm install".

Then, to run the back-end project, write "npm start"

Setting up the table in MySQL

  1. script file  
  2. for student_tbl[code language = "sql"]  
  3. CREATE TABLE IF NOT EXISTS `student_tbl` (`rno`  
  4.     int(11) NOT NULL, `name`  
  5.     varchar(50) DEFAULT NULL, `mobile_no`  
  6.     varchar(15) DEFAULT NULL, `student_img`  
  7.     varchar(1000) NOT NULLPRIMARY KEY(`rno`)) ENGINE = InnoDB DEFAULT CHARSET = latin1;   

Back-end using node.js

First, I will start from creating the back-end using node.js. If you want to know the steps for how to create REST API using node.js, you can go here.

So first, I will create dbconnection.js file which will store the hostname, username, and database name. 

  1. var mysql = require('mysql');  
  2. var connection = mysql.createPool({  
  3.     host: 'localhost',  
  4.     user: 'root',  
  5.     password: '',  
  6.     database: 'demo'  
  7. });  
  8. module.exports = connection;   

Now, in the next step, I will create the student_model inside Models directory. It contains all the methods for fetching, inserting, and deleting the student records from the database.

  1. var db = require('../dbconnection');  
  2. var fs = require('fs');  
  3. var Student = {  
  4.     getAllStudent: function(callback) {  
  5.         return db.query("select * from student_tbl", callback);  
  6.     },  
  7.     deleteStudent: function(Student, callback) {  
  8.         if (Student.student_img != '') {  
  9.             var path = './public' + Student.student_img;  
  10.             fs.unlink(path, function(err) {  
  11.                 if (err) {  
  12.                     console.log(err);  
  13.                 }  
  14.                 console.log('Deleted successfuly')  
  15.             });  
  16.         }  
  17.         return db.query("delete from student_tbl where rno=?", [Student.rno], callback);  
  18.     },  
  19.     addStudent: function(Student, callback) {  
  20.         var dt = new Date(); //current date and time of server  
  21.         var text = ""//random text  
  22.         var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";  
  23.         for (var i = 0; i < 5; i++) text += possible.charAt(Math.floor(Math.random() * possible.length));  
  24.         var base64d = Student.student_img.replace(/^data:image\/png;base64,/, "");  
  25.         var path = "./public/images/" + text + dt.getDate() + dt.getMonth() + dt.getMilliseconds() + ".png";  
  26.         var path1 = "/images/" + text + dt.getDate() + dt.getMonth() + dt.getMilliseconds() + ".png";  
  27.         fs.writeFile(path, base64d, 'base64'function(err) {  
  28.             if (err) {  
  29.                 return console.log(err);  
  30.             }  
  31.             console.log("The file was saved!");  
  32.         });  
  33.         return db.query("Insert into student_tbl values(?,?,?,?)", [Student.rno, Student.name, Student.mobile_no, path1], callback);  
  34.     }  
  35. };  
  36. module.exports = Student;   

First, I need to import dbconnection.js and file system references to student_model.js file. In the above code, the first method "getAllStudent" simply returns all the records from the student_tbl.

The next method, "deleteStudent" deletes the record from the table and also deletes the images that are previously uploaded by the user using file system.
 
And, the last method "addStudent" adds new record to database and also uploads the images into the folder. Here, I am passing byte[] from body and creating image from byte[].

Now, set the routes in app.js. 

  1. var express = require('express');  
  2. var path = require('path');  
  3. var favicon = require('serve-favicon');  
  4. var logger = require('morgan');  
  5. var cookieParser = require('cookie-parser');  
  6. var bodyParser = require('body-parser');  
  7. var cors = require('cors');  
  8. var index = require('./routes/index');  
  9. var users = require('./routes/users');  
  10. var Students = require('./routes/Students');  
  11. var app = express();  
  12. // view engine setup  
  13. app.set('views', path.join(__dirname, 'views'));  
  14. app.set('view engine''jade');  
  15. // uncomment after placing your favicon in /public  
  16. //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));  
  17. app.use(cors());  
  18. app.use(logger('dev'));  
  19. app.use(bodyParser.json({  
  20.     limit: '50mb'  
  21. }));  
  22. app.use(bodyParser.urlencoded({  
  23.     limit: '50mb',  
  24.     extended: false  
  25. }));  
  26. app.use(cookieParser());  
  27. app.use(express.static(path.join(__dirname, 'public')));  
  28. app.use('/', index);  
  29. app.use('/users', users);  
  30. app.use('/Students', Students);  
  31. // catch 404 and forward to error handler  
  32. app.use(function(req, res, next) {  
  33.     var err = new Error('Not Found');  
  34.     err.status = 404;  
  35.     next(err);  
  36. });  
  37. // error handler  
  38. app.use(function(err, req, res, next) {  
  39.     // set locals, only providing error in development  
  40.     res.locals.message = err.message;  
  41.     res.locals.error = req.app.get('env') === 'development' ? err : {};  
  42.     // render the error page  
  43.     res.status(err.status || 500);  
  44.     res.render('error');  
  45. });  
  46. module.exports = app;  

Now, one important thing in app.js is that I am defining the limit of data. By default, it is set to 1 MB. So, I have extended it to 50 MB.

All is set up for the back-end, now it’s turn to set up the front-end in Angular 2.

Front-end using Angular 2

I will start from creating data structure for student.

student.ts 

  1. export class Student {  
  2.     public constructor(public rno: number, public name: string, public mobile_no: string, public student_img: string) {}  
  3. }   

I have created one class for students which contains 4 columns like rno, name, mobile_no, and student_img.

Now, I will create a component to add a new student.

addstudent.component.html 

  1. <div class="container">  
  2.     <form (ngSubmit)="studentSubmit()" #addform="ngForm">  
  3.         <div class="form-group"> <label for="id">rollno</label> <input type="number" [(ngModel)]="model.rno" name="rno" class="form-control" id="rno" required #rno="ngModel"> </div>  
  4.         <div [hidden]="rno.valid || rno.pristine" class="alert alert-danger"> Roll No is required </div>  
  5.         <div class="form-group"> <label for="name">Name</label> <input type="text" [(ngModel)]="model.name" name="name" class="form-control" id="name" required #name="ngModel"> </div>  
  6.         <div [hidden]="name.valid || name.pristine" class="alert alert-danger"> Student Name is required </div>  
  7.         <div class="form-group"> <label for="name">Mobile Number</label> <input type="text" [(ngModel)]="model.mobile_no" name="mobile_no" class="form-control" id="mobile_no" required #mobile_no="ngModel"> </div>  
  8.         <div [hidden]="mobile_no.valid || mobile_no.pristine" class="alert alert-danger"> Student Mobile Number is required </div>  
  9.         <div class="form-group"> <input type="file" name="student_img" required (change)="fileChange(input)" #input /> </div>  
  10.         <div> <img [attr.src]='file_srcs[0]' alt="" /> </div> <button type="submit" class="btn btn-default form-control" [disabled]="!addform.form.valid">Add Task</button> </form>  
  11. </div>  

Here, in the above HTML, I used <input type=”file” />, and created filechange() method to create a preview of selected file.

addstudent.component.ts 

  1. import {  
  2.     Component,  
  3.     OnInit,  
  4.     ChangeDetectorRef  
  5. } from '@angular/core';  
  6. import {  
  7.     Student  
  8. } from './student';  
  9. import {  
  10.     StudentdataService  
  11. } from './studentdata.service';  
  12. import {  
  13.     Router  
  14. } from '@angular/router';  
  15. @Component({  
  16.     selector: 'app-addstudent',  
  17.     templateUrl: './addstudent.component.html',  
  18.     styleUrls: ['./addstudent.component.css']  
  19. })  
  20. export class AddstudentComponent implements OnInit {  
  21.     model = {  
  22.         rno: 0,  
  23.         name: '',  
  24.         mobile_no: '',  
  25.         student_img: ''  
  26.     };  
  27.     path = '';  
  28.     public file_srcs: string[] = [];  
  29.     public debug_size_before: string[] = [];  
  30.     public debug_size_after: string[] = [];  
  31.     constructor(private changeDetectorRef: ChangeDetectorRef, private studata: StudentdataService, public _route: Router) {}  
  32.     ngOnInit() {}  
  33.     fileChange(input) {  
  34.         this.readFiles(input.files);  
  35.     }  
  36.     readFile(file, reader, callback) {  
  37.         reader.onload = () => {  
  38.             callback(reader.result);  
  39.             this.model.student_img = reader.result;  
  40.             console.log(reader.result);  
  41.         }  
  42.         reader.readAsDataURL(file);  
  43.     }  
  44.     readFiles(files, index = 0) {  
  45.         // Create the file reader  
  46.         let reader = new FileReader();  
  47.         // If there is a file  
  48.         if (index in files) {  
  49.             // Start reading this file  
  50.             this.readFile(files[index], reader, (result) => {  
  51.                 // Create an img element and add the image file data to it  
  52.                 var img = document.createElement("img");  
  53.                 img.src = result;  
  54.                 // Send this img to the resize function (and wait for callback)  
  55.                 this.resize(img, 250, 250, (resized_jpeg, before, after) => {  
  56.                     // For debugging (size in bytes before and after)  
  57.                     this.debug_size_before.push(before);  
  58.                     this.debug_size_after.push(after);  
  59.                     // Add the resized jpeg img source to a list for preview  
  60.                     // This is also the file you want to upload. (either as a  
  61.                     // base64 string or img.src = resized_jpeg if you prefer a file).  
  62.                     this.file_srcs.push(resized_jpeg);  
  63.                     // Read the next file;  
  64.                     this.readFiles(files, index + 1);  
  65.                 });  
  66.             });  
  67.         } else {  
  68.             // When all files are done This forces a change detection  
  69.             this.changeDetectorRef.detectChanges();  
  70.         }  
  71.     }  
  72.     resize(img, MAX_WIDTH: number, MAX_HEIGHT: number, callback) {  
  73.         // This will wait until the img is loaded before calling this function  
  74.         return img.onload = () => {  
  75.             // Get the images current width and height  
  76.             var width = img.width;  
  77.             var height = img.height;  
  78.             // Set the WxH to fit the Max values (but maintain proportions)  
  79.             if (width > height) {  
  80.                 if (width > MAX_WIDTH) {  
  81.                     height *= MAX_WIDTH / width;  
  82.                     width = MAX_WIDTH;  
  83.                 }  
  84.             } else {  
  85.                 if (height > MAX_HEIGHT) {  
  86.                     width *= MAX_HEIGHT / height;  
  87.                     height = MAX_HEIGHT;  
  88.                 }  
  89.             }  
  90.             // create a canvas object  
  91.             var canvas = document.createElement("canvas");  
  92.             // Set the canvas to the new calculated dimensions  
  93.             canvas.width = width;  
  94.             canvas.height = height;  
  95.             var ctx = canvas.getContext("2d");  
  96.             ctx.drawImage(img, 0, 0, width, height);  
  97.             // Get this encoded as a jpeg  
  98.             // IMPORTANT: 'jpeg' NOT 'jpg'  
  99.             var dataUrl = canvas.toDataURL('image/jpeg');  
  100.             // callback with the results  
  101.             callback(dataUrl, img.src.length, dataUrl.length);  
  102.         };  
  103.     }  
  104.     studentSubmit() {  
  105.         this.studata.addStudent(this.model).subscribe(  
  106.             (data: any) => {  
  107.                 this._route.navigate(['/allStudent']);  
  108.             },  
  109.             function(error) {  
  110.                 console.log(error);  
  111.             },  
  112.             function() {  
  113.                 console.log("On Complete");  
  114.             });  
  115.     }  
  116. }  

In the above code, I have created the filechange method which will be fired when file is selected. I have created two more functions for resizing the file and converting the file to base 64. Finally, on clicking the  "Add Student" button, I am submitting the data using post method and on success, it will navigate back to student display page.

students.component.html 

  1. <div class="container">  
  2.     <div class="row"> <button (click)="addStudent()">Add Student</button> <br/>  
  3.         <table class="table">  
  4.             <thead>  
  5.                 <th>RollNo</th>  
  6.                 <th>Name</th>  
  7.                 <th>Mobile Number</th>  
  8.                 <th>Photo</th>  
  9.                 <th>Action</th>  
  10.             </thead>  
  11.             <tbody>  
  12.                 <tr *ngFor="let item of allStudent ">  
  13.                     <td>{{item.rno}}</td>  
  14.                     <td>{{item.name | uppercase}}</td>  
  15.                     <td>{{item.mobile_no}}</td>  
  16.                     <td><span class="thumbnail"><img src="http://localhost:3000{{item.student_img}}" height="150px" width="150px" /></span></td>  
  17.                     <td><button (click)="delStudent(item)"><span class="glyphicon glyphicon-trash"></span></button> </td>  
  18.                 </tr>  
  19.             </tbody>  
  20.         </table>  
  21.     </div>  
  22. </div>  

In this HTML, I am displaying the list of all students.

students.component.ts 

  1. import {  
  2.     Component,  
  3.     OnInit  
  4. } from '@angular/core';  
  5. import {  
  6.     Student  
  7. } from './student';  
  8. import {  
  9.     StudentdataService  
  10. } from './studentdata.service';  
  11. import {  
  12.     Router  
  13. } from '@angular/router';  
  14. @Component({  
  15.     selector: 'app-students',  
  16.     templateUrl: './students.component.html',  
  17.     styleUrls: ['./students.component.css']  
  18. })  
  19. export class StudentsComponent implements OnInit {  
  20.     allStudent: Student[] = [];  
  21.     constructor(private _studata: StudentdataService, private _route: Router) {}  
  22.     ngOnInit() {  
  23.         this._studata.getAllStudent().subscribe(  
  24.             (data: Student[]) => {  
  25.                 this.allStudent = data;  
  26.             },  
  27.             function(error) {  
  28.                 console.log(error);  
  29.             },  
  30.             function() {  
  31.                 console.log("complete");  
  32.             });  
  33.     }  
  34.     addStudent() {  
  35.         this._route.navigate(['/addStudent']);  
  36.     }  
  37.     delStudent(item: Student) {  
  38.         this._studata.deleteStudent(item).subscribe(  
  39.             (data: any) => {  
  40.                 this.allStudent.splice(this.allStudent.indexOf(item), 1);  
  41.             },  
  42.             function(error) {  
  43.                 console.log(error);  
  44.             },  
  45.             function() {});  
  46.     }  
  47. }  

The above code will loop through the "allStudent" array and display each and every student in HTML.

Add Student


Display Student


Conclusion

This tutorial is in continuation of my Angular 2 series. If you do not understand anything in this article, please go through the other parts of the tutorial. For example, if you are wondering how routing is done, then you can simply find everything here.