在使用 mergeMap 上传所有文件后启动 http post
Starting a http post after all files are uploaded with a mergeMap
我正在尝试在所有文件上传后发出 POST
请求。在我的网络表单中,用户可以添加许多文件来上传。我有两个 API 来处理我的请求。
一个API用于我的fileupload
,它在数据库中保存文件,returns一个id(文档id),第二个API保存档案。在调用 savedossier
API 之前,我必须将所有文档 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)
);
我的问题是第二次调用(对 savedossier$
)没有等到所有文件都上传完毕(因为它是异步的)。所以文档 ID 没有添加到我的档案对象中。
我已经用这样的 "ugly" 解决方案进行了测试:
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有没有更好的解决方案?
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)),
);
如果我没理解错的话,你需要先完成所有的fileupload操作才能保存文档,里面必须包含所有的id
s 由 fileupload 操作返回。
我还假设 this.attachments
是某种包含附件名称的数组。
如果这一切都是真的,我会这样做。
首先,我将创建一个 Observable 数组,每个代表您要进行的 http 调用以保存附件并接收其 id
作为响应
const saveAttachments = this.attachments).map(
file => this.http.post<Attachement>(`${this.addAttUrl}`, file),
);
然后您可以使用 forkJoin
像这样
并行执行所有这些 observable
forkJoin(saveAttachments)
其中 returns 一个 Observable,它在 saveAttachments
数组中作为参数传递的所有 observable 完成时发出。发出的值是一个数组,其中包含返回的所有 id
。
现在您可以执行最后一个操作,即保存档案,将 savedossier$
可观察到的与上述 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
after[=49= 的函数返回的可观察对象] 之前的可观察对象,即 forkJoin
返回的对象,已经完成。
如果您想了解有关如何在不同用例中使用 Observables 组合 http 调用的一些模式,this article may be of interest。
我正在尝试在所有文件上传后发出 POST
请求。在我的网络表单中,用户可以添加许多文件来上传。我有两个 API 来处理我的请求。
一个API用于我的fileupload
,它在数据库中保存文件,returns一个id(文档id),第二个API保存档案。在调用 savedossier
API 之前,我必须将所有文档 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)
);
我的问题是第二次调用(对 savedossier$
)没有等到所有文件都上传完毕(因为它是异步的)。所以文档 ID 没有添加到我的档案对象中。
我已经用这样的 "ugly" 解决方案进行了测试:
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有没有更好的解决方案?
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)),
);
如果我没理解错的话,你需要先完成所有的fileupload操作才能保存文档,里面必须包含所有的id
s 由 fileupload 操作返回。
我还假设 this.attachments
是某种包含附件名称的数组。
如果这一切都是真的,我会这样做。
首先,我将创建一个 Observable 数组,每个代表您要进行的 http 调用以保存附件并接收其 id
作为响应
const saveAttachments = this.attachments).map(
file => this.http.post<Attachement>(`${this.addAttUrl}`, file),
);
然后您可以使用 forkJoin
像这样
forkJoin(saveAttachments)
其中 returns 一个 Observable,它在 saveAttachments
数组中作为参数传递的所有 observable 完成时发出。发出的值是一个数组,其中包含返回的所有 id
。
现在您可以执行最后一个操作,即保存档案,将 savedossier$
可观察到的与上述 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
after[=49= 的函数返回的可观察对象] 之前的可观察对象,即 forkJoin
返回的对象,已经完成。
如果您想了解有关如何在不同用例中使用 Observables 组合 http 调用的一些模式,this article may be of interest。