1

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?

Patrick
  • 5,526
  • 14
  • 64
  • 101

2 Answers2

2

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
    }
  });
ruth
  • 29,535
  • 4
  • 30
  • 57
  • 2
    Whoo boy winner winner chicken dinner :) I'll accept when I can, blocked on how fast you responded. – Patrick Jan 18 '22 at 10:52
0

Maybe it help someone Solution

I take for base this example and add calculating percentage for downloaded file.

  1. Your server must send content as Blob (binary)
  2. In request headers server must send header Content-length: 789(other count file size in bytes). I lost many time for understand what is really happen in server and what is missing for me to know file size

In this example you can't see what happened visually, because file very small. But open in dev-tools and set throttling and you can see. With large files you can see calculating download in percentage, because packages split and sends in chunks.

forest smoker
  • 119
  • 1
  • 3