Angular uses two HTTP events to handle this:
https://angular.io/api/common/http/HttpEventType
UploadProgress and DownloadProgress. You also need to include reportProgress: true as a parameter on the optional section of your http method.
(Note: for multiple upload/downloads at the same time you would want to use an array of subjects and forkjoin them together at the end. Alexander can explain it better here: https://medium.com/angular-in-depth/rxjs-recipes-forkjoin-with-the-progress-of-completion-for-bulk-network-requests-in-angular-5d585a77cce1
public upload(file: File): Observable<any> {
const result = new Subject<any>(); //the subject that will track progress
const fileUploadResult = {
percentComplete: 0, // the property you will have emitted to show progress.
error: false // you can also add on error, result, or any other variable needed
};
result.next(fileUploadResult);
const formData: FormData = new FormData();
formData.append('file', file, file.name);
const req = new HttpRequest('POST', this.endpointURL, formData, {
reportProgress: true, // IMPORTANT: flag to tell it to report the progress
responseType: 'text'
});
this.httpClient.request(req).subscribe(
event => {
if (event.type === HttpEventType.UploadProgress) { // if progress, get the % and emit it.
const percentComplete = Math.round(100 * event.loaded / event.total) - 1;
result.next({
percentComplete,
error: false
});
} else if (event instanceof HttpResponse) {
result.next({
percentComplete: 100,
error: false,
result: event.body
});
result.complete();
}
}, error => this.handleError(error, result));
return result;
}
So anyways, what you do is return the subject, or you can be safe an return an observable return result as Observable<any>;
handleError in my case is something like this, but it needs to get the subject to be able to emit to it:
private handleError(error: HttpErrorResponse, result: Subject<FileUploadResult>) {
const x: any = error;
try {
const errorResponse = JSON.parse(JSON.parse(x).message);
result.next({
percentComplete: 100,
error: true,
errorMessage: errorResponse.message,
errors: errorResponse.errors
});
} catch {
result.next({
percentComplete: 100,
error: true
});
}
result.complete();
The actual subscription to such a method looks like this (I have my example doing a bit more special case handling, but hopefully the idea comes across):
this.uploadService.upload(this.file).subscribe(
r => {
this.progress = r;
this.showDelete = false;
if (r.percentComplete === 100) {
this.uploadComplete = true;
if (r.error) {
this.bar.nativeElement.classList.add('file-upload__upload-failure');
this.progress = null;
title = 'Upload Failed';
if (this.basicError) {
message = 'The upload failed. Please try again later.';
} else {
if (r.errors) {
errorList = r.errors;
}
if (r.errorMessage) {
message = r.errorMessage;
} else {
message = 'The upload failed. Please try again later.';
}
}
} else {
this.successBanner = true;
Hope this helps some people, Happy Coding!++