RESTFul File Upload in Angular

RESTFul file upload in Angular: In my last post titled RESTFul file upload using Spring Boot, as the title stands for, I have shared how to do the same. As requested by some of my friends, in this post, I will be sharing the front-end aspect of the functionality.

This post is about RESTFul file upload using Angular – typescript-based web-development framework. Angular has state-of-the-art features for a wide range of applications.

The application features a progress bar to show the progress of the file upload. If you are looking for how to implement file upload with a progress bar, you are in the right direction. Follow the content below.

This is what we are going to build. The Angular application would interact with the backend endpoints built using Spring Boot.

An
File upload in Angular

Create a new angular project to implement file upload

ng new project-name

I have added Bootstrap and JQuery to the project. There are many ways to do it. You may do following the best suits you.

Here is what I have done. I used these two npm install commands:

npm install bootstrap
npm install jquery

Then I added the following lines of code in angular.json.

"styles": [
      "src/styles.css",
      "node_modules/bootstrap/dist/css/bootstrap.min.css"
       ],
"scripts": [
        "node_modules/jquery/dist/jquery.min.js",
         "node_modules/bootstrap/dist/js/bootstrap.min.js"
]

Creating a component for file upload

ng generate component file-upload

Component HTML code

From the two types of forms, Angular provides, I am going to make use of a reactive form. When the submit button is clicked the file upload method will be triggered.

<nav class="navbar navbar-expand-lg navbar-light bg-light">
    <a class="navbar-brand" href="http://bhutanio.com">Bhutan IO</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
  
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav mr-auto">
        <li class="nav-item active">
          <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="#">Link</a>
        </li>
      </ul>
      <form class="form-inline my-2 my-lg-0">
        <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
        <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
      </form>
    </div>
  </nav>
  <div class="container">
    <div class="row">
      <div class="col-md-3"></div>
      <div class="col-md-6">
        <h1>Trying RESTFul File Upload</h1>
        <form [formGroup]="fileUploadForm" (ngSubmit)="fileUpload()">
          <div class="mb-3">
            <label for="fileControl" class="form-label">Select file</label>
            <input type="file" class="form-control" id="fileControl" (change)="selectFile($event)">
          </div>

          <div class="form-group">
            <div *ngIf="currentFile" class="progress">
              <div class="progress-bar bg-primary progress-bar-striped" role="progressbar"
                attr.aria-valuenow="{{ progress }}" aria-valuemin="0" aria-valuemax="100"
                [ngStyle]="{ width: progress + '%' }">
                {{ progress }}%
              </div>
            </div>
          </div>
          
          <div *ngIf="message" class="alert alert-danger" role="alert">
            {{message}}
          </div>
          <div class="form-group">
            <div class="card"> 
              <div class="card-header">List of Files</div>
              <table class="table table-bordered table-hover">
                <thead>
                  <tr>
                    <th>File Name</th>
                    <th>Remove</th>
                  </tr>
                </thead>
                <tbody>
                  <tr *ngFor="let file of fileInfos | async; let i = index">
                    <td><a href="{{ file.url }}">{{ file.name }}</a></td>
                    <td>
                      <button class="btn btn-xs btn-danger" (click)="deleteByPath(file.name)">Delete</button>
                    </td>
                  </tr>
                </tbody>
              </table>
            </div>
          </div>
          
          <button type="submit" class="btn btn-primary">Submit</button>
        </form>
      </div>
      <div class="col-md-3"></div>
    </div>
  </div>

Component Typescript Code

import { HttpEventType, HttpResponse } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
import { FileUploadService } from './file-upload.service';

@Component({
  selector: 'app-file-upload',
  templateUrl: './file-upload.component.html',
  styleUrls: ['./file-upload.component.css']
})
export class FileUploadComponent implements OnInit {
  fileUploadForm: FormGroup;
  selectedFiles: FileList;
  currentFile: File;
  progress = 0;
  message = '';
  fileInfos: Observable<any>;
  url = '/files';

  constructor(
    private formBuilder: FormBuilder,
    private fileUploadService: FileUploadService) { }

  ngOnInit(): void {
    this.fileUploadForm = this.formBuilder.group({
      // form controls if required
    });
    this.fileInfos = this.fileUploadService.getFiles(this.url);
  }

  fileUpload(){
    this.progress = 0;
    this.currentFile = this.selectedFiles.item(0);
    
    this.fileUploadService.upload(this.url, this.currentFile).subscribe(
      event => {
        if (event.type === HttpEventType.UploadProgress) {
          this.progress = Math.round(100 * event.loaded / event.total);
        } else if (event instanceof HttpResponse) {
          this.message = event.body.message;
          this.fileInfos = this.fileUploadService.getFiles(this.url);
        }
      },
      err => {
        this.progress = 0;
        this.message = 'Could not upload the file!';
        this.currentFile = undefined;
      });
    this.selectedFiles = undefined;
  }

  deleteByPath(path: string){
    alert(path)
    this.fileUploadService.deleteByPath(path, this.url).subscribe(event => {
      if (event instanceof HttpResponse) {
        this.message = event.body.message;
        this.fileInfos = this.fileUploadService.getFiles(this.url);
      }
    },
    err => {
      this.message = 'Could not delete the file!';
    });
  }

  selectFile(event: any) {
    this.selectedFiles = event.target.files;
  }

}

Create file upload service

Create/generate a service using Angular CLI command:

ng generate service file-upload

File upload service code

import { HttpClient, HttpEvent, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class FileUploadService {

  private baseUrl = 'http://localhost:8080';

  constructor(private http: HttpClient,) { }
  upload(url: string, file: File): Observable<HttpEvent<any>> {
    alert(file)
    const formData: FormData = new FormData();
    formData.append('file', file);

    const req = new HttpRequest('POST', `${this.baseUrl}${url}/upload`, formData, {
      reportProgress: true,
      responseType: 'json'
    });

    return this.http.request(req);
  }

  getFiles(url: string): Observable<any> {
    return this.http.get(`${this.baseUrl}${url}/getfiles`);
  }

  deleteByPath(path: string, url: string): Observable<HttpEvent<any>> {
    const formData: FormData = new FormData();
    formData.append('path', path);
    const req = new HttpRequest('POST', `${this.baseUrl}${url}/delete`, formData, {
      reportProgress: true,
      responseType: 'json'
    });
    return this.http.request(req);
  }
}
I hope it helped you!

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *