429 请求太多 - Angular 7 - 关于多个文件上传
429 Too Many Requests - Angular 7 - on multiple file upload
当我尝试同时上传超过几百个文件时出现此问题。
API 接口仅适用于一个文件,因此我必须调用发送每个文件的服务。现在我有这个:
onFilePaymentSelect(event): void {
if (event.target.files.length > 0) {
this.paymentFiles = event.target.files[0];
}
let i = 0;
let save = 0;
const numFiles = event.target.files.length;
let procesed = 0;
if (event.target.files.length > 0) {
while (event.target.files[i]) {
const formData = new FormData();
formData.append('file', event.target.files[i]);
this.payrollsService.sendFilesPaymentName(formData).subscribe(
(response) => {
let added = null;
procesed++;
if (response.status_message === 'File saved') {
added = true;
save++;
} else {
added = false;
}
this.payList.push({ filename, message, added });
});
i++;
}
}
所以我确实有一段时间将每个文件发送到 API,但我在大量文件上收到消息“429 请求太多”。我有什么办法可以改进吗?
使用可观察对象将使该任务更易于推理(而不是使用命令式编程)。
浏览器通常允许您并行发出 6 个请求,并将其他请求排队。但是我们不希望浏览器为我们管理该队列(或者如果我们 运行 在节点环境中,我们不会为 ex 管理该队列)。
我们要什么:我们要上传很多文件。它们应该通过 运行 始终并行 5 个请求尽可能高效地排队和上传。 (因此我们为应用中的其他请求保留 1 个免费)。
为了演示,让我们先构建一些模拟:
function randomInteger(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
const mockPayrollsService = {
sendFilesPaymentName: (file: File) => {
return of(file).pipe(
// simulate a 500ms to 1.5s network latency from the server
delay(randomInteger(500, 1500))
);
}
};
// array containing 50 files which are mocked
const files: File[] = Array.from({ length: 50 })
.fill(null)
.map(() => new File([], ""));
我认为上面的代码是不言自明的。我们正在生成模拟,这样我们就可以看到代码的核心实际上如何 运行 而无需真正访问您的应用程序。
现在,主要部分:
const NUMBER_OF_PARALLEL_CALLS = 5;
const onFilePaymentSelect = (files: File[]) => {
const uploadQueue$ = from(files).pipe(
map(file => mockPayrollsService.sendFilesPaymentName(file)),
mergeAll(NUMBER_OF_PARALLEL_CALLS)
);
uploadQueue$
.pipe(
scan(nbUploadedFiles => nbUploadedFiles + 1, 0),
tap(nbUploadedFiles =>
console.log(`${nbUploadedFiles}/${files.length} file(s) uploaded`)
),
tap({ complete: () => console.log("All files have been uploaded") })
)
.subscribe();
};
onFilePaymentSelect(files);
- 我们使用
from
将文件一个一个发送到一个observable
- 使用
map
,我们准备了对 1 个文件的请求(但由于我们没有订阅它并且 observable 是冷的,请求只是准备好了,没有触发!)
- 我们现在使用
mergeMap
到 运行 呼叫池。由于 mergeMap
将并发作为参数,我们可以说 "please run a maximum of 5 calls at the same time"
- 然后我们使用
scan
仅用于显示目的(计算已成功上传的文件数)
这是一个现场演示:https://stackblitz.com/edit/rxjs-zuwy33?file=index.ts
打开控制台可以看到我们没有一次上传所有文件
当我尝试同时上传超过几百个文件时出现此问题。
API 接口仅适用于一个文件,因此我必须调用发送每个文件的服务。现在我有这个:
onFilePaymentSelect(event): void {
if (event.target.files.length > 0) {
this.paymentFiles = event.target.files[0];
}
let i = 0;
let save = 0;
const numFiles = event.target.files.length;
let procesed = 0;
if (event.target.files.length > 0) {
while (event.target.files[i]) {
const formData = new FormData();
formData.append('file', event.target.files[i]);
this.payrollsService.sendFilesPaymentName(formData).subscribe(
(response) => {
let added = null;
procesed++;
if (response.status_message === 'File saved') {
added = true;
save++;
} else {
added = false;
}
this.payList.push({ filename, message, added });
});
i++;
}
}
所以我确实有一段时间将每个文件发送到 API,但我在大量文件上收到消息“429 请求太多”。我有什么办法可以改进吗?
使用可观察对象将使该任务更易于推理(而不是使用命令式编程)。
浏览器通常允许您并行发出 6 个请求,并将其他请求排队。但是我们不希望浏览器为我们管理该队列(或者如果我们 运行 在节点环境中,我们不会为 ex 管理该队列)。
我们要什么:我们要上传很多文件。它们应该通过 运行 始终并行 5 个请求尽可能高效地排队和上传。 (因此我们为应用中的其他请求保留 1 个免费)。
为了演示,让我们先构建一些模拟:
function randomInteger(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
const mockPayrollsService = {
sendFilesPaymentName: (file: File) => {
return of(file).pipe(
// simulate a 500ms to 1.5s network latency from the server
delay(randomInteger(500, 1500))
);
}
};
// array containing 50 files which are mocked
const files: File[] = Array.from({ length: 50 })
.fill(null)
.map(() => new File([], ""));
我认为上面的代码是不言自明的。我们正在生成模拟,这样我们就可以看到代码的核心实际上如何 运行 而无需真正访问您的应用程序。
现在,主要部分:
const NUMBER_OF_PARALLEL_CALLS = 5;
const onFilePaymentSelect = (files: File[]) => {
const uploadQueue$ = from(files).pipe(
map(file => mockPayrollsService.sendFilesPaymentName(file)),
mergeAll(NUMBER_OF_PARALLEL_CALLS)
);
uploadQueue$
.pipe(
scan(nbUploadedFiles => nbUploadedFiles + 1, 0),
tap(nbUploadedFiles =>
console.log(`${nbUploadedFiles}/${files.length} file(s) uploaded`)
),
tap({ complete: () => console.log("All files have been uploaded") })
)
.subscribe();
};
onFilePaymentSelect(files);
- 我们使用
from
将文件一个一个发送到一个observable - 使用
map
,我们准备了对 1 个文件的请求(但由于我们没有订阅它并且 observable 是冷的,请求只是准备好了,没有触发!) - 我们现在使用
mergeMap
到 运行 呼叫池。由于mergeMap
将并发作为参数,我们可以说 "please run a maximum of 5 calls at the same time" - 然后我们使用
scan
仅用于显示目的(计算已成功上传的文件数)
这是一个现场演示:https://stackblitz.com/edit/rxjs-zuwy33?file=index.ts
打开控制台可以看到我们没有一次上传所有文件