Angular File Upload (With Upload Progress)


Sometimes you need to upload a file with the Angular framework.

And send it to a specific API server.

And while the file is being you want to track the progress of that upload and display the progress to the user.

image

Here's a straightforward guide to using Angular file input forms, uploading a file to a backend API and output the progress of the upload.

Step 1: Set Up the Angular File Input

Our first step is to use the HTML file input and bind it's change event.

<input type="file" (change)="onFileSelected($event)" />

And then we need to handle the file selection event inside our components Typescript file.

selectedFile: File | null = null;

onFileSelected(event: Event): void {
    const input = event.target as HTMLInputElement;
    if (input.files && input.files.length > 0) {
        this.selectedFile = input.files[0];
    }
}

Step 2: Upload the File to the Backend API

Next we'll need to inject Angular's HttpClient and handle the HTTP request to our API.

import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core';

constructor(private http: HttpClient) {}

Or if you prefer using the inject function.

import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core';

const http = inject(HttpClient);

Now we'll define our upload method that checks if a file is selected. If selected it will use FormData to upload it via our HTTP client.

uploadFile(): void {
    if (!this.selectedFile) {
        return;
    }
    const formData = new FormData();
    formData.append('file', this.selectedFile);

    this.http.post('https://api.blackhole.io/upload-file', formData).subscribe(
        response => console.log('File uploaded successfully:', response),
        error => console.error('File upload failed:', error)
    );
}

And now we'll add a button in the HTML template to trigger the file upload.

<button (click)="uploadFile()">Upload File</button>

Step 3: Track the progress of the upload

Now we'll define a get event message method that we'll pipe the results of the upload results to monitor progress.

private getEventMessage(event: HttpEvent<any>) {
    switch (event.type) {
        case HttpEventType.UploadProgress:
            const percentDone = event.total ? Math.round((100 * event.loaded) / event.total) : 0;
            return percentDone;
        case HttpEventType.Response:
            return '100';
        default:
            return '';
    }
}

And now we'll modify the upload call to report the progress.

uploadFile(): void {
    if (!this.selectedFile) {
        return;
    }
    const formData = new FormData();
    formData.append('file', this.selectedFile);

    const req = new HttpRequest('POST', 'https://api.blackhole.io/upload-file', formData, { reportProgress: true });

    this.http.request(req)
    .pipe(
        map((progressEvent) => this.getEventMessage(progressEvent)),
        tap((progress) => console.log(progress)),
        last(),
    )    
    .subscribe(
        response => console.log('File uploaded successfully:', response),
        error => console.error('File upload failed:', error)
    );
}

Full Example Code

And here is our final code.

Our HTML template file.

<input type="file" (change)="onFileSelected($event)" />
<button (click)="uploadFile()">Upload File</button>

Along with the Typescript implementation.

import { Component } from '@angular/core';
import { HttpClient, HttpEvent, HttpEventType, HttpRequest } from '@angular/common/http';
import { last, map, tap } from 'rxjs';

@Component({
  selector: 'app-file-upload',
  templateUrl: './file-upload.component.html'
})
export class FileUploadComponent {
  selectedFile: File | null = null;

  constructor(private http: HttpClient) {}

  onFileSelected(event: Event): void {
    const input = event.target as HTMLInputElement;
    if (input.files && input.files.length > 0) {
      this.selectedFile = input.files[0];
    }
  }

  uploadFile(): void {
    if (!this.selectedFile) {
        return;
    }
    const formData = new FormData();
    formData.append('file', this.selectedFile);

    const req = new HttpRequest('POST', 'https://api.blackhole.io/upload-file', formData, { reportProgress: true });

    this.http.request(req)
    .pipe(
        map((progressEvent) => this.getEventMessage(progressEvent)),
        tap((progress) => console.log(progress)),
        last(),
    )    
    .subscribe(
        response => console.log('File uploaded successfully:', response),
        error => console.error('File upload failed:', error)
    );
  }

  private getEventMessage(event: HttpEvent<any>) {
    switch (event.type) {
        case HttpEventType.UploadProgress:
            const percentDone = event.total ? Math.round((100 * event.loaded) / event.total) : 0;
            return percentDone;
        case HttpEventType.Response:
            return '100';
        default:
            return '';
    }
  }
}

And that, my friend, is how to upload a file with the Angular framework and track the progress.

Till next time,

signature

Daniel Kreider