client/server multiple file upload made easy using express-fileupload (mean-stack)

File upload is a process where a client requests to upload files to the server just as t works on Facebook and Instagram.

prerequisite: this tutorial require you already a basic understanding of server-side application and nodejs.


There are so many modules you can use to upload files to the server, but I will be using  express-fileupload.  and my client application is angular and the server is nodejs.

1. Installation
You can visit the link to learn more about express-fileupload. However, the following command will install express-fileupload.
npm install express-fileupload

Next, is my client's (angular material step form) view where I will upload company details along with five documents.



The HTML code below the preview screenshot above.
<mat-horizontal-stepper [linear]="isLinear" #stepper>
    
    <mat-step >
       <!-- contact details -->
      <form #contactForm="ngForm" name="contactForm" class="form"  >
        <ng-template matStepLabel>contacts</ng-template>
        <mat-form-field>
          <mat-label>ComPany name</mat-label>
          <input matInput readonly #companyName placeholder="company name" type="text"
          name="companyName" [(ngModel)]="companyModel.companyName" required>
        </mat-form-field>
        <mat-form-field>
          <mat-label>ComPany Address</mat-label>
          <input matInput #companyAddress placeholder="company address" type="text"
          name="companyAddress" [(ngModel)]="companyModel.companyAddress" required>
        </mat-form-field>
        <mat-form-field>
          <mat-label>ComPany Phome</mat-label>
          <input matInput #companyPhone placeholder="company phone" type="text"
          name="companyPhone" [(ngModel)]="companyModel.companyPhone" required>
        </mat-form-field>
        <mat-form-field>
          <mat-label>Contact mail</mat-label>
          <input matInput #contactMail placeholder="company mail" type="email"
          name="contactMail" [(ngModel)]="companyModel.contactMail" required>
        </mat-form-field>
        <div>
          <button color="primary" [disabled]="!contactForm.valid"  mat-button matStepperNext>Next</button>
        </div>
      </form>
    
    </mat-step>


    <mat-step >
      <!-- company cac certificate -->
      <form #detailForm="ngForm" name="detailForm" class="form" >
        <ng-template matStepLabel>details</ng-template>
        <mat-form-field>
          <mat-label>Rc numbers</mat-label>
          <input matInput #rcNumber placeholder="registration number" type="text"
          name="rcNumber" [(ngModel)]="companyModel.rcNumber" required>
        </mat-form-field>
       <ion-row>
         <ion-col>
          
          <small class="ion-margin">cac certificate</small>
          <ion-item >
            <ion-input  #cacCertificate  type="file"  (change)="cacCertInput($event)"
            name="cacCertificate" accept=".pdf" [(ngModel)]="companyModel.cacCertificate" required></ion-input>
          </ion-item>
         </ion-col>
       </ion-row>
       <mat-form-field>
        <mat-label>cac status</mat-label>
        <input matInput #cacCertStatus placeholder=" approved" type="text"
        name="cacCertStatus" [(ngModel)]="companyModel.cacCertStatus" required>
      </mat-form-field>
        
        <div>
          <button color="warn" mat-button matStepperPrevious>Back</button>
          <button color="primary" [disabled]="!detailForm.valid" mat-button matStepperNext>Next</button>
        </div>
      </form>
    </mat-step>

    <mat-step  >
      <!--  award form -->
      <form #awardForm="ngForm" name="awardForm" class="form" >
        <ng-template matStepLabel>award</ng-template>
       <ion-row>
         <ion-col>
          
          <small class="ion-margin">award letter</small>
          <ion-item >
            <ion-input  #awardLatter  type="file"  (change)="awardLatterInput($event)"
            name="awardLatter" accept=".pdf" [(ngModel)]="companyModel.awardLetter" required></ion-input>
          </ion-item>
         </ion-col>
       </ion-row>
       <mat-form-field>
        <mat-label>award leter status</mat-label>
        <input matInput #cacCertStatus placeholder="approved" type="text"
        name="awardLetterStatus" [(ngModel)]="companyModel.awardLetterStatus" required>
      </mat-form-field>
        
        <div>
          <button color="warn" mat-button matStepperPrevious>Back</button>
          <button color="primary" [disabled]="!awardForm.valid" mat-button matStepperNext>Next</button>
        </div>
      </form>
    </mat-step>

    <mat-step  >
      <!--  contract form -->
      <form #contractForm="ngForm" name="contractForm" class="form" >
        <ng-template matStepLabel>contract</ng-template>

        
       <mat-form-field>
        <mat-label>number of contract</mat-label>
        <input matInput #numberOfContracts placeholder="" type="text"
        name="numberOfContracts" [(ngModel)]="companyModel.numberOfContract" required>
      </mat-form-field>
       <ion-row>
         <ion-col>
          <small class="ion-margin">contract latter</small>
          <ion-item >
            <ion-input  #contractLatter  type="file"  (change)="contractLatterInput($event)"
            name="contractLatter" accept=".pdf" [(ngModel)]="companyModel.contractLetter" required></ion-input>
          </ion-item>
         </ion-col>
       </ion-row>
       <mat-form-field>
        <mat-label>contract later status</mat-label>
        <input matInput #contractLatterStatus placeholder="" type="text"
        name="contractLatterStatus" [(ngModel)]="companyModel.contractLetterStatus" required>
      </mat-form-field>
        
        <div>
          <button color="warn" mat-button matStepperPrevious>Back</button>
          <button color="primary" [disabled]="!contractForm.valid" mat-button matStepperNext>Next</button>
        </div>
      </form>
    </mat-step>

    <mat-step  >
      <!--  tax form -->
      <form #taxForm="ngForm" name="taxForm" class="form" >
        <ng-template matStepLabel>tax</ng-template>
       <ion-row>
         <ion-col>
          
          <small class="ion-margin">tax clearance</small>
          <ion-item >
            <ion-input  #taxClearance  type="file"  (change)="taxClearanceInput($event)"
            name="taxClearance" accept=".pdf" [(ngModel)]="companyModel.taxClearance" required></ion-input>
          </ion-item>
         </ion-col>
       </ion-row>
       <mat-form-field>
        <mat-label>tax document status</mat-label>
        <input matInput #taxClearanceStatus placeholder="" type="text"
        name="taxClearanceStatus" [(ngModel)]="companyModel.taxClearanceStatus" required>
      </mat-form-field>
        
        <div>
          <button color="warn" mat-button matStepperPrevious>Back</button>
          <button color="primary" [disabled]="!taxForm.valid" mat-button matStepperNext>Next</button>
        </div>
      </form>
    </mat-step>

    <mat-step  >
      <!--  mou -->
      <form #mouForm="ngForm" name="mouForm" class="form" >
        <ng-template matStepLabel>mou</ng-template>
       <ion-row>
         <ion-col>
          
          <small class="ion-margin">mou document</small>
          <ion-item >
            <ion-input  #mouFile  type="file"  (change)="mouFileInput($event)"
            name="mouFile" accept=".pdf" [(ngModel)]="companyModel.mouFile"
  required></ion-input>
          </ion-item>
         </ion-col>
       </ion-row>
       <mat-form-field>
        <mat-label>mou document status</mat-label>
        <input matInput #mouFileStatus placeholder="approved" type="text"
        name="mouFileStatus" [(ngModel)]="companyModel.mouFileStatus" required>
      </mat-form-field>
        
        <div>
          <button color="warn" mat-button matStepperPrevious>Back</button>
          <button color="primary" [disabled]="!mouForm.valid" mat-button
  matStepperNext>Next</button>
        </div>
      </form>
    </mat-step>

    <mat-step  >
      <!--  position -->
      <form #positionForm="ngForm" name="positionForm" class="form" >
        <ng-template matStepLabel>position</ng-template>
      
       <mat-form-field>
        <mat-label>your position</mat-label>
        <input matInput #yourPosition placeholder="" type="text"
        name="yourPosition" [(ngModel)]="companyModel.yourPosition" required>
      </mat-form-field>
      <mat-form-field>
        <mat-label>your name</mat-label>
        <input matInput #yourName placeholder="" type="text"
        name="yourName" [(ngModel)]="companyModel.yourName" required>
      </mat-form-field>
      <ion-col>
        <mat-form-field>
          <mat-label>director 1</mat-label>
          <input matInput #director1 placeholder="" type="text"
          name="director1" [(ngModel)]="companyModel.director1" required>
        </mat-form-field>
        <mat-form-field>
          <mat-label>director 1 phone</mat-label>
          <input matInput #director1Phone placeholder="" type="text"
          name="director1Phone" [(ngModel)]="companyModel.director1Phone" required>
        </mat-form-field>
      </ion-col>

      <ion-col>
        <mat-form-field>
          <mat-label>director 2</mat-label>
          <input matInput #director2 placeholder="" type="text"
          name="director2" [(ngModel)]="companyModel.director2" required>
        </mat-form-field>
        <mat-form-field>
          <mat-label>director 2 phone</mat-label>
          <input matInput #director2Phone placeholder="" type="text"
          name="director2Phone" [(ngModel)]="companyModel.director2Phone" required>
        </mat-form-field>
      </ion-col>
        
        <div>
          <button color="warn" mat-button matStepperPrevious>Back</button>
          <ion-button color="success" [disabled]="!positionForm.valid"
 (click)="submitRecord()">finish</ion-button>
        </div>
      </form>
    </mat-step>

  </mat-horizontal-stepper>

the above form is binded to the following form
companyModel = { companyName:''rcNumber:'',  companyAddress:'',
  companyPhone:'',contactMail:''numberOfContract:'',cacCertificate:null,
  cacCertStatus:''awardLetter:nullawardLetterStatus:''contractLetter:null,
  contractLetterStatus:''mouFile:nullmouFileStatus:''taxClearance:null,
  taxClearanceStatus:''yourPosition:''yourName:'',director1:''director2:'',
  director1Phone:''director2Phone:''
  }

Next, after filling our form, on submission we will pass the model values to formData() below
 submitRecord(){
    console.log(this.companyModel);
    const formData = new FormData();
    formData.append('companyName'this.companyModel.companyName);
    formData.append('rcNumber'this.companyModel.rcNumber);
    formData.append('companyAddress'this.companyModel.companyAddress);
    formData.append('companyPhone'this.companyModel.companyPhone);
    formData.append('contactMail'this.companyModel.contactMail);
    formData.append('numberOfContract'this.companyModel.numberOfContract);
    formData.append('cacCertificate'this.companyModel.cacCertificate,  this.companyModel.cacCertificate.name);
    formData.append('cacCertStatus'this.companyModel.cacCertStatus);
    formData.append('awardLetter'this.companyModel.awardLetter,  this.companyModel.awardLetter.name);
    formData.append('awardLetterStatus'this.companyModel.awardLetterStatus);
    formData.append('contractLetter'this.companyModel.contractLetter,  this.companyModel.contractLetter.name);
    formData.append('contractLetterStatus'this.companyModel.contractLetterStatus);
    formData.append('mouFile'this.companyModel.mouFile,  this.companyModel.mouFile.name);
    formData.append('mouFileStatus'this.companyModel.mouFileStatus);
    formData.append('taxClearance'this.companyModel.taxClearancethis.companyModel.taxClearance.name);
    formData.append('taxClearanceStatus'this.companyModel.taxClearanceStatus);
    formData.append('yourPosition'this.companyModel.yourPosition);
    formData.append('yourName'this.companyModel.yourName);
    formData.append('director1'this.companyModel.director1);
    formData.append('director2'this.companyModel.director2);
    formData.append('director1Phone'this.companyModel.director1Phone);
    formData.append('director2Phone'this.companyModel.director2Phone);

    this.companyService.submitCompanyRecord(formData).subscribe(
      res => {
        console.log(res);
        this.userService.generalToastShort('success'res['msg']);
        this.clearForm();
        setTimeout(()=> {
          this.router.navigateByUrl('/my-company');
        })
      },
      err => {
        console.log(err);
        this.userService.generalToast('error'err.error.msg);
      }
    );
  }


the following is an API to submit the formdata housing our files and documents.

submitCompanyRecord(comRecord){
    return this.http.post(environment.apiBaseUrl + '/company/submit-record'comRecord); 
  }

Next is where is an API endpoint which will save our document. As expected you should have created a general folder for the incoming files and a model for JSON object.

The first thing to do is to import all the necessary modules.

const express = require("express");
const mongoose = require('mongoose');
const router = express.Router();
const fs = require('fs');
const path = require('path');
const jwt_helper = require('../config/jwt_helper');

const CompanyModel = mongoose.model('companyModel');

Next, I will check if the incoming request has incoming else I will send and error messages with a status code of 400.


router.post('/submit-record',jwt_helper.verifyJwtTokenasync (reqres)=>{
    
    if (!req.files || Object.keys(req.files).length === 0) {
        return res.status(400).send({msg:'No files were uploaded.'});

      }else{

if everything is ok, I want to remove white space from the company's name and create a folder with that folder name with space.
 const  company_folder =  req.body.companyName.toLowerCase().split(' ').join('_');
        console.log('com folder',company_folder);

        try{
            // crete company's folder
            await fs.promises.mkdir(`data/${company_folder}`, { recursive: true })
        } catch(err){
            console.log(err'ERROR CREATING COMPANY FOLDER');
        }

Next, we will save all necessary body object using companyModel instance.

        var newCompanyRecord =  new CompanyModel();
        newCompanyRecord.user_id =  req._id;
        newCompanyRecord.companyName =  req.body.companyName;
        newCompanyRecord.rcNumber =  req.body.rcNumber;
        newCompanyRecord.companyAddress =  req.body.companyAddress;
        newCompanyRecord.companyPhone =  req.body.companyPhone;
        newCompanyRecord.contactMail =  req.body.contactMail;
        newCompanyRecord.numberOfContract =  req.body.numberOfContract;
        // cac certificate
        newCompanyRecord.cacCertificate.name =  req.files.cacCertificate.name;  
        newCompanyRecord.cacCertificate.status =  req.body.cacCertStatus;
        newCompanyRecord.cacCertificate.url =  `data/${company_folder}/`+req.files.cacCertificate.name;
        //  award letter
        newCompanyRecord.awardLetter.name =   req.files.awardLetter.name;
        newCompanyRecord.awardLetter.status =  req.body.awardLetterStatus;
        newCompanyRecord.awardLetter.url =  `data/${company_folder}/`+req.files.awardLetter.name;
         //  award letter
        newCompanyRecord.contractLetter.name =   req.files.contractLetter.name;
        newCompanyRecord.contractLetter.status =  req.body.contractLetterStatus;
        newCompanyRecord.contractLetter.url =  `data/${company_folder}/`+req.files.contractLetter.name;

         //  mou letter
        newCompanyRecord.mouFile.name =  req.files.mouFile.name;
        newCompanyRecord.mouFile.status =  req.body.mouFileStatus;
        newCompanyRecord.mouFile.url =  `data/${company_folder}/`+req.files.mouFile.name;

         //  taxClearance letter
        newCompanyRecord.taxClearance.name =   req.files.taxClearance.name;
        newCompanyRecord.taxClearance.status =  req.body.taxClearanceStatus;
        newCompanyRecord.taxClearance.url =  `data/${company_folder}/`+req.files.taxClearance.name;
        
        newCompanyRecord.yourPosition =  req.body.yourPosition;
        newCompanyRecord.yourName =  req.body.yourName;
        console.log('contact mail',req.body.contactMail);
        newCompanyRecord.contactEmail =  req.body.contactEmail;
        newCompanyRecord.director1.name =  req.body.director1;
        newCompanyRecord.director1.phone =  req.body.director1Phone;
        newCompanyRecord.director2.name =  req.body.director2;
        newCompanyRecord.director2.phone =  req.body.director2Phone;
        newCompanyRecord.save().then((record)=> {

            console.log(record);
         
        res.status(200).send({msg:' record has been saved!'});
        })

      }
} ); 


router.get('/get-company'jwt_helper.verifyJwtTokenasync (reqres)=> {
   
   await CompanyModel.find({user_id: req._id}).then((record)=> {
       if(!record){
          res.status(404).send({msg:'no record found'});
       }else{
         
          res.status(200).send({record: record});
       }
   });

if your upload process successful, you will get a success response at the client-side and your metadata saved in database and files saved in a folder. data/company name.

So this is you can upload multiple file/ form object using express-fileupload.

Post a Comment

0 Comments