Angular / RxJs: прогресс загрузки

У меня есть рабочее решение для получения прогресса загрузки в Angular 13/RxJs 7.

Сначала я определил некоторые перечисления:

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

export enum ActionType {
  download = 1,
  upload
}

Затем я реализовал общий сервис для отслеживания прогресса:

      @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;

    }
  }

}

Затем я просто подписываюсь на это и получаю правильный ход загрузки:

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

Все это отлично работает, я получаю индикатор выполнения с моей библиотекой компонентов, который показывает реальный прогресс.

Вопрос... как мне получить результирующий файл?

2 ответа

Вместо того, чтобы возвращать только ход выполнения, вы можете вернуть объект, содержащий процент выполнения и ответ HTTP.

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

Теперь в подписке можно использовать объект

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

Может кому поможет Решение

Я беру за основу этот пример и добавляю расчет процента за скачанный файл.

  1. Ваш сервер должен отправлять контент в виде Blob (двоичного файла).
  2. В заголовках запросов сервер должен отправить заголовокContent-length: 789(other count file size in bytes). Я потерял много времени, чтобы понять, что на самом деле происходит на сервере и чего мне не хватает, чтобы узнать размер файла.

В этом примере визуально увидеть, что произошло, невозможно, поскольку файл очень маленький. Но откройте инструменты разработчика и установите регулирование, и вы увидите. Для больших файлов вы можете увидеть расчет загрузки в процентах, поскольку пакеты разделяются и отправляются частями.

Другие вопросы по тегам