0

我正在尝试POST在所有文件上传后提出请求。在我的 Web 表单中,用户可以添加许多要上传的文件。我有两个处理我的请求的 API。

我的一个 APIfileupload将文件保存在数据库中并返回一个 id(文档 ID),第二个 API 保存档案。在调用savedossierAPI 之前,我必须将所有文档 ID 添加到档案对象。

我用mergeMap上传文件:

const fileupload$ = from(this.attachments).pipe(
  mergeMap(file => this.http.post<Attachement>(`${this.addAttUrl}`, file)),
);

fileupload$.subscribe(
  (data) => this.dossier.attachments.push(data),
  (err) => console.error('err: ' + err)
);

const savedossier$ = this.http.post<Dossier>(`${this.adddossierUrl}`, this.dossier);

savedossier$.subscribe(
  (data) => console.log('Dossier saved!'),
  (err) => console.error('err: ' + err)
);

我的问题是第二次调用 (to savedossier$) 不会等到所有文件都上传完毕(因为它是异步的)。因此,文档 ID 不会添加到我的档案对象中。

我用这样的“丑陋”解决方案进行了测试:

const fileupload$ = from(this.attachments).pipe(
  mergeMap(file => this.http.post<Attachement>(`${this.addAttUrl}`, file)),
);

fileupload$.subscribe(
  (data) => onuploaded(data),
  (err) => console.error('err: ' + err)
);

onuploaded(data) {
  this.dossier.attachments.push(data)
  if (this.dossier.attachments.length === this.attachments.length) {
    savedossier$.subscribe(
      (data) => console.log('Dossier saved!'),
      (err) => console.error('err: ' + err)
    );
  }
}

const savedossier$ = this.http.post<Dossier>(`${this.adddossierUrl}`, this.dossier);

带有比较数组长度的 if 语句的此解决方案有效。但我认为这不是推荐的方式。

rxjs 有更好的解决方案吗?

4

2 回答 2

1

如果我理解正确,您需要先完成所有fileupload操作,然后再保存文档,该文档必须包含fileuploadid操作返回的所有 s 。

我还假设这this.attachments是某种包含附件名称的数组。

如果这一切都是真的,我会这样进行。

首先,我将创建一个 Observable 数组,每个都代表您要进行的 http 调用以保存附件并接收其id作为响应

const saveAttachments = this.attachments).map(
  file => this.http.post<Attachement>(`${this.addAttUrl}`, file),
);

然后,您可以forkJoin像这样并行执行所有这些 observables

forkJoin(saveAttachments)

saveAttachments它返回一个 Observable,当数组中作为参数传递的所有 observable 都完成时发出。id发出的值是一个返回所有 s的数组。

现在您可以执行最后一个操作,即保存档案,将savedossier$observable 连接到上面返回的那个forkJoin。最终的代码看起来像这样

const saveAttachments = this.attachments).map(
  file => this.http.post<Attachement>(`${this.addAttUrl}`, file),
);
forkJoin(saveAttachment).pipe(
   concatMap(ids => {
      this.dossier.attachments.push(data);
      return this.http.post<Dossier>(`${this.adddossierUrl}`, this.dossier);
   })
)

请注意,您必须使用concatMap运算符来指定您要执行作为参数传递给的函数返回的可观察对象,concatMap 在前一个可观察对象(即由返回的对象forkJoin)完成之后。

如果您想了解一些关于如何在不同用例中使用 Observables 组合 http 调用的模式,这篇文章可能会很有趣

于 2020-05-06T13:45:04.567 回答
1

您可以使用switchMap点击运算符:

const fileAndSaveDossier$ = from(this.attachments).pipe(
  mergeMap(file => this.http.post<Attachement>(`${this.addAttUrl}`, file)),
  tap(data => this.dossier.attachments.push(data)),
  switchMap(() => this.http.post<Dossier>(`${this.adddossierUrl}`, this.dossier)),
);

fileAndSaveDossier$.subscribe(
  (data) => console.log('Dossier saved!'),
  (err) => console.error('err: ' + err)
);

一个很好的视频解释switchMap()https ://www.youtube.com/watch?v=6lKoLwGlglE

我也不确定你为什么要使用mergeMap(). 你可以这样做:

const fileAndSaveDossier$ = this.http.post<Attachement>(
  `${this.addAttUrl}`, this.attachments
).pipe(
  tap(data => this.dossier.attachments.push(data)),
  switchMap(() => this.http.post<Dossier>(`${this.adddossierUrl}`, this.dossier)),
);
于 2020-05-06T10:10:55.190 回答