Q:

File upload progress checking if file send as FormData in Angular 7?

I uploading file as FormData in Angular 7 with this code using HttpClient as http:
sendImageFile(subUri: string, id: number, fileToUpload: File): Observable<any> {
  const formData: FormData = new FormData();
  formData.append('file', fileToUpload, fileToUpload.name);
  formData.append('photoalbum_id', id.toString() );
  // ... some other .append()

  const customHeaders = new HttpHeaders({
    'Authorization': 'Bearer' + localStorage.getItem('token'),
    'Accepted-Encoding': 'application/json'
  });

  const customOptions = {
    headers: customHeaders,
    reportProgress: true
  };

  return this.http.post(this.url, formData, customOptions)
    .pipe(
      map( (event: HttpEvent<any>) => this.getEventMessage(event, fileToUpload)),
      tap(message => this.showProgress(message)),
      // last(),
      catchError(this.handleError));
}

private showProgress(message: any) {
  // ...
}

private getEventMessage(event: HttpEvent<any>, file: File) {
  // ...
}

The main problem is here isn't checking the uploading because the file is uploaded part of the FormData so I don't get any feedback until the upload is finished.
I'm a little bit confused about this progress checking. I must to upload file as FormData. How can I check the upload progress in this case?

A:

The answer lies in HttpClient's implementation. All requests created via the HttpClient.post method default the observe property to body. See this HttpClient.post method for details. What this means is: even though you successfully set reportProgress to true, the resulting observable is observing the request body instead of the HttpEvents. From the docs (emphasis mine):

The observe value determines the return type of request(), based on what the consumer is interested in observing. A value of events will return an Observable<HttpEvent> representing the raw HttpEvent stream, including progress events by default. A value of response will return an Observable<HttpResponse<T>> where the T parameter of HttpResponse depends on the responseType and any optionally provided type parameter. A value of body will return an Observable<T> with the same T body type.

The documentation also notes that if you pass an HttpRequest instance to request, it will return an observable of the HttpEvent stream by default:

This method can be called in one of two ways. Either an HttpRequest instance can be passed directly as the only parameter, or a method can be passed as the first parameter, a string URL as the second, and an options hash as the third.
If a HttpRequest object is passed directly, an Observable of the raw HttpEvent stream will be returned.

So, the easiest way to observe the HttpEvent stream is to pass an HttpRequest object directly as noted:
sendImageFile(subUri: string, id: number, fileToUpload: File): Observable<any> {
  const formData: FormData = new FormData();
  formData.append('file', fileToUpload, fileToUpload.name);
  formData.append('photoalbum_id', id.toString());
  // ... some other .append()

  const customHeaders = new HttpHeaders({
    'Authorization': 'Bearer' + localStorage.getItem('token'),
    'Accepted-Encoding': 'application/json'
  });

  const customOptions = {
    headers: customHeaders,
    reportProgress: true,
  };

  const req = new HttpRequest('POST', this.url, formData, customOptions);

  // Call HttpClient.request with an HttpRequest as only param to get an observable of HttpEvents
  return this.http.request(req)
    .pipe(
      map((event: HttpEvent<any>) => this.getEventMessage(event)),
      catchError(this.handleError));
}

private getEventMessage(event: HttpEvent<any>) {
  // We are now getting events and can do whatever we want with them!
  console.log(event);
}

I tested this refactored code on a local repository and it worked just fine.

A:

EDITED :
Due to the fact that Angular grows , as I proposed in my previous answer - the npm package would not suit your needs. So here is what you can actually do : 
private getEventMessage(event: HttpEvent<any>, file: File) {
  switch (event.type) {
    case HttpEventType.Sent:
      return `Uploading file "${file.name}" of size ${file.size}.`;

    case HttpEventType.UploadProgress:
      // Compute and show the % done:
      const percentDone = Math.round(100 * event.loaded / event.total);
      return `File "${file.name}" is ${percentDone}% uploaded.`;

    case HttpEventType.Response:
      return `File "${file.name}" was completely uploaded!`;

    default:
      return `File "${file.name}" surprising upload event: ${event.type}.`;
  }
}

and then you can  modify your showProgress() function according to the returned values of getEventMessage() . Does this help ? 

