1

下面是调用下面函数的代码片段。这是一个允许人们将文件从系统拖放到网站的过程。它显示所有文件的列表,并在加载时带有进度条。它在大多数情况下都可以正常工作,但是当有大量文件时,我会遇到一些问题。我有一个正在加载的测试目录,其中包含 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;
  }
4

2 回答 2

1

为了forkJoin完成,您必须确保所有提供的 observables 都已完成。可能发生的情况是forkJoin订阅Subjects为时已晚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
  • 文件 observables 的订阅只发生在一次地方,在forkJoin

this.http.request将在请求完成时完成,因此forkJoin也应该能够完成,允许您进行“清理”(删除加载进度等......)。

于 2020-04-01T08:55:14.040 回答
0

我接受了上面@Andrei 的回答,因为如果不是他,我可能永远不会停止用头撞墙。我在下面包含了我最终使用的实际代码,作为将来可能遇到类似问题的任何其他人的参考。可能还可以进行许多改进,但至少它有效。

if (this.files.size > 0) {
  const status: { [key: string]: { progress: BehaviorSubject<number> } } = {};
  const allProgressObservables = [];
  for (let f of this.files) {
    const progress = new BehaviorSubject<number>(0);
    status[f.fullPath] = {progress: progress};
    allProgressObservables.push(status[f.fullPath].progress);
  }
  this.sfUploadSnackbar.openSnackbar(this.files, status);
  forkJoin(allProgressObservables)
    .subscribe(() => {
      this.sfUploadSnackbar.closeSnackbar();
    });
  from(this.files)
    .pipe(
      mergeMap(async (file) => {
        let params = new HttpParams()
          .set('dir', this.currentDir)
          .set('path', file.fullPath.replace(file.name,''))
          .set('projectId', this.currentProject.toString())
          .set('timestamp', timestamp.toString());
        let f: File = await new Promise((resolve, reject) => file.file(resolve))
        const formData: FormData = new FormData();
        formData.append('file', f, f.name);
        const req = new HttpRequest('POST', '/api/dndUpload', formData, {
          reportProgress: true, params
        });
        return this.http.request(req)
          .subscribe(event => {
            if (event.type === HttpEventType.UploadProgress) {
              const percentDone = Math.round(100 * event.loaded / event.total);
              status[file.fullPath].progress.next(percentDone);
            } else if (event instanceof HttpResponse) {
              status[file.fullPath].progress.complete();
            }  
          });
      })
    ).subscribe();
}
于 2020-04-07T19:28:10.223 回答