429 слишком много запросов - Angular 7 - при загрузке нескольких файлов - PullRequest
1 голос
/ 06 февраля 2020

У меня возникает эта проблема, когда я пытаюсь загрузить более нескольких сотен файлов одновременно.

Интерфейс 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 слишком много запросов» для большого количества файлов. Как я могу улучшить это?

1 Ответ

2 голосов
/ 06 февраля 2020

Работа с наблюдаемыми позволяет упростить эту задачу (вместо использования императивного программирования).

Браузер обычно позволяет вам сделать 6 запросов параллельно и поставит в очередь другие. Но мы не хотим, чтобы браузер управлял этой очередью для нас (или если мы работаем в среде узлов, у нас не будет такой для бывших).

Что мы хотим: мы хотим загрузить много файлов. Их следует ставить в очередь и загружать как можно более эффективно, выполняя 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 для отправки файлов один за другим в наблюдаемую
  • , используя map, мы подготавливаем наш запрос на 1 файл (но так как мы не подписываемся на него, и наблюдаемая становится холодной, запрос только что подготовлен, а не запущен!)
  • теперь мы используем mergeMap для запуска пула вызовов. Благодаря тому, что mergeMap принимает параллелизм в качестве аргумента, мы можем сказать: «Пожалуйста, выполните максимум 5 вызовов одновременно»
  • , тогда мы используем scan только для отображения (для подсчета количество файлов, которые были успешно загружены)

Вот демонстрационная версия: https://stackblitz.com/edit/rxjs-zuwy33?file=index.ts

Откройте консоль, чтобы видите, мы не загружаем их все сразу

...