Процесс загрузки файла не обновляется в наблюдаемой или завершается до того, как наблюдаемая будет готова - PullRequest
1 голос
/ 01 апреля 2020

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

Правильно ли настроен объект? Есть ли лучший способ отслеживать прогресс загружаемых файлов? Любая помощь будет оценена.

if (this.files.size > 0) {
  this.progress = await this.uploadService.dndUpload(
    this.files, this.currentDir, this.currentProject, timestamp
  );
  let allProgressObservables = [];
  for (let key in this.progress) {
    allProgressObservables.push(this.progress[key].progress);
  }

  this.sfUploadSnackbar.openSnackbar(this.files, this.progress);

  forkJoin(allProgressObservables).subscribe(async end => {
    this.sfUploadSnackbar.closeSnackbar();
    this.uploadService.clearUploadDir(this.currentProject, timestamp)
      .subscribe();
    this.uploadInProgress = false;
    this.getFiles();
  });
}





 async dndUpload(files: Set<any>, dir: string, projectId: number, timestamp: number) {
    const status: { [key: string]: { progress: Observable<number> } } = {};

    for (let it = files.values(), file = null; file = it.next().value;) {

      let params = new HttpParams()
        .set('dir', dir)
        .set('path', file.fullPath.replace(file.name,''))
        .set('projectId', projectId.toString())
        .set('timestamp', timestamp.toString());
      let f: File = await new Promise((resolve, reject) => file.file(resolve, reject))
      const formData: FormData = new FormData();
      formData.append('file', f, f.name);

      const req = new HttpRequest('POST', '/api/dndUpload', formData, {
        reportProgress: true, params
      });

      const progress = new Subject<number>();

      status[file.name] = {
        progress: progress.asObservable()
      };

      this.http.request(req).subscribe(event => {
        if (event.type === HttpEventType.UploadProgress) {

          const percentDone = Math.round(100 * event.loaded / event.total);

          progress.next(percentDone);
        } else if (event instanceof HttpResponse) {

          progress.complete();
        }
      });
    }

    return status;
  }

Ответы [ 2 ]

1 голос
/ 01 апреля 2020

Чтобы завершить forkJoin, необходимо убедиться, что все предоставленные наблюдаемые завершены. Может случиться так, что forkJoin подписывается слишком поздно на Subject с allProgressObservables.

. Я предполагаю, что this.sfUploadSnackbar.openSnackbar(this.files, this.progress); подпишется на this.progress, чтобы получать проценты для каждого файла.

Вот идея:

dndUpload (files: Set<...>/* ... */): Observable<any> {
  // Note that there are no subscriptions
  return [...files].map(
    f => from(new Promise((resolve, reject) => file.file(resolve, reject)))
      .pipe(
        map(f => (new FormData()).append('file', f, f.name)),
      )
  )
}

const fileObservables$ = this.dndUpload(files);

const progressObservables$ = fileObservables$.map(
  (file$, fileIdx) => file$.pipe(
    switchMap(formData => {
      const req = /* ... */;

      return this.http.request(req)
        .pipe(
          filter(event.type === HttpEventType.UploadProgress),
          // Getting the percent
          map(e => Math.round(100 * e.loaded / e.total)),
          tap(percent => this.updatePercentVisually(percent, fileIdx))
        )
    })
  )
);

// Finally subscribing only once to the observables
forkJoin(progressObservables$).subscribe(...);

Обратите внимание, что есть несколько изменений:

  • Я перестал использовать Subject для каждого файла
    • как следствие, this.sfUploadSnackbar.openSnackbar(this.files, this.progress); пришлось заменить другим подходом (this.updatePercentVisually)
  • подписка на наблюдаемые файлы происходит только в одном месте, в forkJoin;

this.http.request завершится, когда запрос будет выполнен, поэтому forkJoin также сможет его выполнить, что позволит вам выполнять «очистки» (удаление процесса загрузки и т. Д. c ...) .

0 голосов
/ 07 апреля 2020

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

...