Profile Image Upload Functionality Using Angular and Node

In a recent project, I was working on a user profile component that can be used by the user to edit their profile information. A part of that component was a profile image section which lets the user change their profile picture. The profile picture is sent to the Node API via HTTP Post request, stored in a folder and then name/URL of the image is stored in the database. In this post, I will share the code on how to implement this functionality.

Form For the Profile Image

We have created an Edit Profile component and added the following code in the edit-profile.component.html to create a form.

<form enctype="multipart/form-data">
    <input type="file" id="profile-pic-input" (change)="detectFiles($event)" accept="image/*"/>
</form>
<button (click)="uploadSingle()" color="accent" [disabled]="!selectedFiles">
    <span>Upload Image</span>
</button>

We have created a simple form with input to grab the image file from the user. When a user clicks the “Choose File” button and selects the image file then detectFiles() method is triggered which is defined inside the edit-profile.component.ts file and it assigns the selected file to the selectedFiles variable. When the file is assigned the “Upload Image” button is enabled. When a user clicks the “Upload Image” button the uploadSingle() method is triggered which is defined in the edit-profile.component.ts file as shown below:

Relevant Code From edit-profile.component.ts File

//other code here
import { HttpClient } from '@angular/common/http';
import { HttpEvent, HttpEventType } from '@angular/common/http';
//other code here
selectedFiles: FileList;
uid: number;
constructor(private fileService: FileUploadService, private http: HttpClient) {
//other code here
this.uid = 123; //get the uid from the auth object
}
// other code here
detectFiles(event){
  this.selectedFiles = event.target.files;
}
//other code here
uploadSingle(){
  const uploadData = new FormData();
  let file = this.selectedFiles.item(0);
  if((file.size/1024/1024) < 1){ //check if file size is less than 1 MB
      let fileName = file.name;
      let idxDot = fileName.lastIndexOf(".") + 1;
      let extFile = fileName.substr(idxDot, fileName.length).toLowerCase();
      fileName = fileName + '-' + Date.now().getTime() + extFile;
      if (extFile=="jpg" || extFile=="jpeg" || extFile=="png"){
        uploadData.append('profilePicture', this.selectedFiles, this.selectedFiles.name);
        this.uploadSingleFile(this.uid, uploadData).subscribe((event: HttpEvent<any>) => {
          switch (event.type) {
            case HttpEventType.UploadProgress:
              //track the progress here
              break;
            case HttpEventType.Response:
              {
                //success
                //update user information here
                //show success message here
              }
          }
        }, err => {
             //error while uploading the image
             //show appropriate message here
        });
      }else{
          //the uploaded image is not from the allowed image types
          //show message to user here
      }
    }else{
          //the uploaded image size is greated than 1 MB
          //show appropriate message here
    }
}

uploadSingleFile(uid, file){
    return this.http.post('http://apiurl.com/'+uid+'profile-image', file);
}

Most of the code is commented out for your reference. As explained earlier the detectFiles() method assigns the selected file to the appropriate variable. Then the uploadSingle() method prepares the file and calls uploadSingleFile() method which makes a POST request to our API’s endpoint (Node API) to upload the file on the server and save the URL in the database as shown below.

NodeJS Route to Handle the Post Request

Make sure to install multer package by running the command: npm install –save multer

const express = require('express');
const app = express();
const multer = require('multer'); // handles the file uploading process
const DIR = './uploads/user-profiles'; // where image will be uploaded
//bootstraps the overall process for file upload
let storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, DIR);
  },
  filename: (req, file, cb) => {
    cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
  }
});
let upload = multer({storage: storage});

/* UPDATE user profile image information.*/
app.post('/:id/profile-image', upload.single('profilePicture'), (req, res) => {
  if (!req.file) {
    res.status(404).json({success: false, message: 'No file received!'});
  } else {
    const sql = "UPDATE users SET ? WHERE id = ?";
    const user = {photoURL: '//'+req.headers.host+'/'+req.file.filename};
    conn.query(sql, [user, req.params.id], function (err, result) {
      if (err) return res.status(500).json({success: false, message: err.message});
      return res.status(200).send(JSON.stringify({message: "Profile image is successfuly updated!"}));
    });
  }
});

The multer package is used to handle the file uploading process. When the ‘/profile-image’ route receives a POST request then upload.single(‘profilePicture’) method is called a middleware that uploads the file and then inside the body of the POST request method we have saved the image URL inside the MySQL database.

That’s it! I hope it would be helpful.

Leave a Comment