Search code examples
angulartypescriptrxjsangular13rxjs7

Angular / RxJs: Download Progress


I have working solution to getting download progress in Angular 13 / RxJs 7.

First I defined some enums:

export enum RequestType {
  get = 'GET',
  post = 'POST',
  put = 'PUT',
  delete = 'DELETE',
}

export enum ActionType {
  download = 1,
  upload
}

Next, I implemented a shared service to handle tracking progress:

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

  percentDone = 0;
  actionType: ActionType;

  constructor(private http: HttpClient) { }

  downloadFile(uri: string, requestType: RequestType, actionType: ActionType, body: any): Observable<number> {

    this.actionType = actionType;

    const req = new HttpRequest(requestType, uri, body, {
      reportProgress: true,
      responseType: 'blob'
    });

    return this.http
      .request(req)
      .pipe(
        map(event => this.getPercentage(event)),
    );

  }

  public getPercentage(event: HttpEvent<any>): number {

    switch (event.type) {

      case HttpEventType.UploadProgress:

      // Downloading files will still trigger upload progress due to the back-and-forth, ignore this
      if (this.actionType !== ActionType.upload) {
        return 0;
      }

      // Compute and show the % done:
        if (event && event.total) {
          this.percentDone = Math.round(100 * event.loaded / event.total);
          return this.percentDone;
        } else {
          return 0;
        }

      case HttpEventType.DownloadProgress:

        if (event && event.total) {
          this.percentDone = Math.round(100 * event.loaded / event.total);
        }

        return this.percentDone;

      default:

        // Not an event we care about
        return this.percentDone;

    }
  }

}

Then, I simply subscribe to this and get proper progress of the download:

this.downloadService
  .downloadFile(url, RequestType.post, ActionType.download, body)
  .subscribe(progress => this.progress = progress);

This all works great, I get a progress bar with my component library that shows real progress.

The question is ... how do I get the resulting file?


Solution

  • Instead of returning only the progress you could return an object that contains the progress percentage and the HTTP response.

    return this.http.request(req).pipe(
      map(event => ({
        progress: this.getPercentage(event),
        response: event
      })),
    );
    

    Now in the subscription you can you use the object

    this.downloadService
      .downloadFile(url, RequestType.post, ActionType.download, body)
      .subscribe({
        next: ({progress, response}) => {
          this.progress = progress;
          // use response
        },
        error: (error: any) => {
          // handle errors
        }
      });