Angular File Upload Example
March 14, 2020
This page will walk through Angular single and multiple file upload example with upload progress. To select file we need to create HTML input with type="file"
. After file selection we can access file content using event.target.files
on change event. We can also use document.getElementById
to get file contents.
To post data, we can use
HttpClient.post
method and pass its options as observe: 'events'
and reportProgress: true
so that we can track upload progress.
On this page we will create reactive form and use
FormArray
to provide UI for multiple file upload. On form submit we will display file upload progress for every selected file. Now let us discuss file upload example step-by-step.
Contents
1. Technologies Used
Find the technologies being used in our example.1. Angular 9.0.2
2. Node.js 12.5.0
3. NPM 6.9.0
2. File Input
In HTML 5, we can create file input to select or drag and drop files.<input type="file">
multiple
attribute as following.
<input type="file" multiple>
1. Using
$event.target.files
with onChange
event.
<input type="file" (change)="upload($event.target.files)">
document.getElementById
. Suppose we have a file input with an id.
<input type="file" id="myFile">
document.getElementById
to read file contents as following.
const selectedFileList = (<HTMLInputElement>document.getElementById('myFile')).files;
multiple
attributes in file input. To get the file use array index.
const file = selectedFileList.item(0);
file.name
and file size can be obtained by file.size
.
3. Using FormData
TheFormData
is a web API to represent HTML form data as key/value. It is ceated as following.
const formData = new FormData();
FormData
has a method as append()
that appends key/value to the FormData
. We will append our File
object with key.
formData.append("file", file);
4. Post Data with Progress
We can post ourFormData
as following.
uploadWithProgress(formData: FormData): Observable<any> { return this.http.post(this.url, formData, { observe: 'events', reportProgress: true }); }
observe
defines how we want response, for example, response
, body
or events
. The events
is for response with events. To get the progress, use reportProgress
as true.
When we subscribe the
Observable
instance, we can get progress and upload success as following.
this.fuService.uploadWithProgress(formData) .subscribe(event => { if (event.type === HttpEventType.UploadProgress) { this.percentCompleted = Math.round(100 * event.loaded / event.total); } else if (event instanceof HttpResponse) { this.isUploaded = true; } });
event.loaded
and event.total
.
HttpResponse: It represents a full HTTP response. If event is the instance of
HttpResponse
, it means posting of data is completed.
HttpUploadProgressEvent
While uploading the content, the
event
in subscribe()
is of HttpUploadProgressEvent
type. The HttpUploadProgressEvent
is an upload progress event. It has properties as following.
type: Event type such as
HttpEventType.UploadProgress
.
loaded: Amount uploaded at a time.
total: Total amount to upload.
5. Single File Upload
Find the steps to upload a single file.1. Create a file input. We will upload file on change event.
<input type="file" (change)="upload($event.target.files)"> <div *ngIf="isSingleUploaded" class="success"> File uploaded successfully with Url: <b>{{urlAfterUpload}}</b> </div>
upload(files: File[]) { const formData = new FormData(); formData.append("file", file); this.fuService.uploadWithProgress(formData) .subscribe(event => { if (event.type === HttpEventType.UploadProgress) { this.percentCompleted = Math.round(100 * event.loaded / event.total); } else if (event instanceof HttpResponse) { this.isSingleUploaded = true; this.urlAfterUpload = event.body.link; } }); }
6. Multiple File Upload with Reactive Form
1. To upload multiple files, we will create reactive form. In theFormGroup
, we will create FormArray
.
uploadForm = this.formBuilder.group({ ------ filesToUpload: this.formBuilder.array([ this.formBuilder.control('', [Validators.required]) ]) });
formArrayName
. We can add more form control to upload multiple files.
<form [formGroup]="uploadForm" (ngSubmit)="onFormSubmit()"> ------ <div formArrayName="filesToUpload"> <div *ngFor="let f of filesToUpload.controls; index as i"> <input [formControlName]="i" type="file" id="file{{i}}"> {{percentUploaded[i]}}% <button type="button" (click)="deleteFile(i)">Delete</button> </div> </div> <button type="button" (click)="addMoreFiles()">Add More Files</button> <button type="submit">Submit</button> </form>
onFormSubmit() { ------ for (let i = 0; i < this.filesToUpload.length && this.uploadForm.valid; i++) { const selectedFileList = (<HTMLInputElement>document.getElementById('file' + i)).files; const file = selectedFileList.item(0); this.uploadFile(file, i); } } uploadFile(file: File, fileNum: number) { const formData = new FormData(); formData.append("file", file); this.fuService.uploadWithProgress(formData) .subscribe(event => { if (event.type === HttpEventType.UploadProgress) { this.percentUploaded[fileNum] = Math.round(100 * event.loaded / event.total); } else if (event instanceof HttpResponse) { console.log(file.name + ', Size: ' + file.size + ', Uploaded URL: ' + event.body.link); this.fileUploadSuccess(); } } ); }
7. Validation
We can validate file upload such as required file selection and valid file extensions. In reactive form for required validation, we useValidators.required
and for valid file extensions, we can create custom validator.
Find the custom validator for valid file extensions.
file-extension-validator.directive.ts
import { ValidatorFn, AbstractControl } from '@angular/forms'; export function fileExtensionValidator(validExt: string): ValidatorFn { return (control: AbstractControl): { [key: string]: any } | null => { let forbidden = true; if (control.value) { const fileExt = control.value.split('.').pop(); validExt.split(',').forEach(ext => { if (ext.trim() == fileExt) { forbidden = false; } }); } return forbidden ? { 'inValidExt': true } : null; }; }
FormGroup
will be as following.
uploadForm = this.formBuilder.group({ ------ filesToUpload: this.formBuilder.array([ this.formBuilder.control('', [Validators.required, fileExtensionValidator('jpg, png, wav, mp4')]) ]) });
<input [formControlName]="i" type="file" id="file{{i}}"> <label *ngIf="filesToUpload.controls[i].errors?.required" class="error"> Select the file. </label> <label *ngIf="filesToUpload.controls[i].errors?.inValidExt && !filesToUpload.controls[i].errors?.required" class="error"> Invalid file extension. </label>
8. Complete Example with Single and Multiple Upload
Find the complete code of our demo application. In this example, we are providing demo for single and multiple upload. For multiple file upload, we will create reactive form. For valid file extension validation, the code forfileExtensionValidator()
, has already been given above in the article. Now find the complete code.
file-upload.service.ts
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class FileUploadService { url = "https://file.io"; constructor(private http: HttpClient) { } uploadWithProgress(formData: FormData): Observable<any> { return this.http.post(this.url, formData, { observe: 'events', reportProgress: true }) .pipe( catchError(err => this.handleError(err)) ); } private handleError(error: any) { return throwError(error); } }
import { Component } from '@angular/core'; import { HttpResponse, HttpEventType } from '@angular/common/http'; import { FormControl, FormArray, FormBuilder, Validators } from '@angular/forms'; import { fileExtensionValidator } from './file-extension-validator.directive'; import { FileUploadService } from './file-upload.service'; @Component({ selector: 'app-upload', templateUrl: 'file-upload.component.html' }) export class FileUploadComponent { percentCompleted: number = 0; isMultipleUploaded = false; isSingleUploaded = false; urlAfterUpload = ''; percentUploaded = [0]; acceptedExtensions = "jpg, jpeg, bmp, png, wav, mp3, mp4"; constructor(private formBuilder: FormBuilder, private fuService: FileUploadService) { } upload(files: File[]) { console.log('---Uploading single file---'); const file = files[0]; console.log(file.name); this.isSingleUploaded = false; this.urlAfterUpload = ''; const formData = new FormData(); formData.append("file", file); this.fuService.uploadWithProgress(formData) .subscribe(event => { if (event.type === HttpEventType.UploadProgress) { this.percentCompleted = Math.round(100 * event.loaded / event.total); } else if (event instanceof HttpResponse) { this.isSingleUploaded = true; this.urlAfterUpload = event.body.link; } }); } uploadForm = this.formBuilder.group({ title: ['', Validators.required], filesToUpload: this.formBuilder.array([ this.formBuilder.control('', [Validators.required, fileExtensionValidator(this.acceptedExtensions)]) ]) }); get title(): FormControl { return this.uploadForm.get('title') as FormControl; } get filesToUpload(): FormArray { return this.uploadForm.get('filesToUpload') as FormArray; } addMoreFiles() { this.filesToUpload.push(this.formBuilder.control('', [Validators.required, fileExtensionValidator(this.acceptedExtensions)])); this.percentUploaded.push(0); } deleteFile(index: number) { this.filesToUpload.removeAt(index); this.percentUploaded.splice(index, 1); } onFormSubmit() { console.log('---Uploading multiple file---'); this.isMultipleUploaded = false; for (let i = 0; i < this.filesToUpload.length && this.uploadForm.valid; i++) { const selectedFileList = (<HTMLInputElement>document.getElementById('file' + i)).files; const file = selectedFileList.item(0); this.uploadFile(file, i); } console.log(this.title.value); } uploadFile(file: File, fileNum: number) { const formData = new FormData(); formData.append("file", file); this.fuService.uploadWithProgress(formData) .subscribe(event => { if (event.type === HttpEventType.UploadProgress) { this.percentUploaded[fileNum] = Math.round(100 * event.loaded / event.total); } else if (event instanceof HttpResponse) { console.log(file.name + ', Size: ' + file.size + ', Uploaded URL: ' + event.body.link); this.fileUploadSuccess(); } }, err => console.log(err) ); } fileUploadSuccess() { let flag = true; this.percentUploaded.forEach(n => { if (n !== 100) { flag = false; } }); if (flag) { this.isMultipleUploaded = true; } } formReset() { this.uploadForm.reset(); this.isMultipleUploaded = false; for (let i = 0; i < this.percentUploaded.length; i++) { this.percentUploaded[i] = 0; } } }
<h3>Single Upload with Progress</h3> <table> <tr> <td> <input type="file" (change)="upload($event.target.files)"> </td> <td> {{percentCompleted}}% Uploaded </td> </tr> <tr> <td colspan="2"> <div *ngIf="isSingleUploaded" class="success"> File uploaded successfully with Url: <b>{{urlAfterUpload}}</b> </div> </td> </tr> </table> <h3>Multiple Upload with Progress</h3> <div *ngIf="isMultipleUploaded" class="success">File uploaded and form submitted successfully. <br/> <br/> </div> <form [formGroup]="uploadForm" (ngSubmit)="onFormSubmit()"> <table> <tr> <td colspan="3"> Title: <input formControlName="title"> <label *ngIf="title.errors?.required" class="error"> Title required. </label> </td> </tr> <div formArrayName="filesToUpload"> <div *ngFor="let f of filesToUpload.controls; index as i"> <tr> <td> <input [formControlName]="i" type="file" id="file{{i}}"> <label *ngIf="filesToUpload.controls[i].errors?.required" class="error"> Select the file. </label> <label *ngIf="filesToUpload.controls[i].errors?.inValidExt && !filesToUpload.controls[i].errors?.required" class="error"> Invalid file extension. </label> </td> <td>{{percentUploaded[i]}}% </td> <td> <button type="button" (click)="deleteFile(i)">Delete</button> </td> </tr> </div> </div> <tr> <td colspan="3"> <button type="submit">Submit</button> <button type="button" (click)="addMoreFiles()">Add More Files</button> <button type="button" (click)="formReset()">Reset</button> </td> </tr> </table> </form>
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: ` <app-upload></app-upload> ` }) export class AppComponent { }
table { border-collapse: collapse; } table, th, td { border: 1px solid black; padding: 10px; } .error { color: red; } .success { color: green; }
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { ReactiveFormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; import { FileUploadComponent } from './file-upload.component'; @NgModule({ imports: [ BrowserModule, ReactiveFormsModule, HttpClientModule ], declarations: [ AppComponent, FileUploadComponent ], providers: [ ], bootstrap: [ AppComponent ] }) export class AppModule { }
9. Run Application
To run the application, find the steps.1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application. To install Angular CLI, find the link.
3. Run ng serve using command prompt.
4. Access the URL http://localhost:4200
Find the print screen using Mozilla Firefox browser.

10. References
HttpClientHttpUploadProgressEvent