Угловая 11 — RxJS конкат для загрузки составных файлов AWS S3 не ожидает завершения запроса

#angular #amazon-s3 #rxjs #rxjs6 #aws-sdk-js

Вопрос:

Я внедряю набор API-интерфейсов для групповой загрузки из пакета SDK AWS S3 для обработки больших загрузок файлов. Согласно документации SDK, загрузка нескольких частей должна быть явно закрыта после загрузки всех частей. Этот вызов API закрытия — CompleteMultipartUpload, требует отображения фрагментов, которое представляет собой отображение поля «Etag», возвращаемого API uploadPart, и номера детали. Поскольку заказ деталей должен быть отображен кодом — я попытался использовать concat, чтобы убедиться, что следующая деталь загружается только после завершения текущей. Однако даже при использовании concat все отдельные части загружаются одновременно и возвращаются в разное время. Это не помогает в идентификации номеров деталей.

Код выглядит так, как показано ниже — я создаю массив наблюдаемых объектов для загружаемых частей и пытаюсь их объединить. Использование RxJS v6.6.3 и AWS SDK версии v2 (в lib-хранилище SDK v3 есть проблема с исполнителем браузера, иначе это сократило бы код).

Почему конкат здесь не работает, это проблема с обработкой массива наблюдаемых объектов?

   const FILE_CHUNK_SIZE = 5 * 1024 * 1024;
  s3SessionToken$ // API call that returns token for S3 upload
  .pipe(
    // CREATE MULTIPART UPLOAD
    concatMap((tokenVal) => {
      // Create S3 Bucket Object
      s3Obj = new S3({
        accessKeyId: tokenVal.accessKeyId,
        secretAccessKey: tokenVal.secretAccessKeyId,
        sessionToken: tokenVal.sessionToken,
      });
      const s3createMultipartUploadParams: S3.CreateMultipartUploadRequest = {
        Bucket: bucketName,
        Key: filePath   file.name,
        ContentType: file.type,
      };

      // Begin Multipart Upload
      return from(
        s3Obj.createMultipartUpload(s3createMultipartUploadParams).promise()
      );
    }),
    // START UPLOADING INDIVIDUAL PARTS
    concatMap(
      (createMultipartUploadOutput: S3.CreateMultipartUploadOutput) => {
        multipartUploadId = createMultipartUploadOutput.UploadId;

        numParts = Math.ceil(file.size / FILE_CHUNK_SIZE);
        console.log('numParts', numParts);

        const uploadPartsObservables: Observable<
          S3.UploadPartOutput
        >[] = [];
        for (let i = 0; i < numParts; i  ) {
          const start = i * FILE_CHUNK_SIZE;
          const end = (i   1) * FILE_CHUNK_SIZE;
          const blob =
            i < numParts ? file.slice(start, end) : file.slice(start);

          const uploadPartParams: S3.UploadPartRequest = {
            Body: blob,
            Bucket: environment.AWS_BUCKET,
            Key: createMultipartUploadOutput.Key,
            PartNumber: i   1,
            UploadId: createMultipartUploadOutput.UploadId,
          };

          uploadPartsObservables.push(
            from(
              s3Obj
                .uploadPart(uploadPartParams)
                .promise()
            )
          );
        }

        return concat(...uploadPartsObservables). // All the uploadPart calls start at the same time
        pipe(
          map((uploadPartResult: S3.UploadPartOutput, index: number) => { // the Index here seems to reflect the order of the response received not the request order
            const PartTagMapObj: S3.Part = {
              ETag: uploadPartResult.ETag,
              PartNumber: index   1,
            };

            return PartTagMapObj;
          }),
          catchError((err) => {
            console.log('error in upload of file : ', err);
            const abortParams: S3.AbortMultipartUploadRequest = {
              Bucket: bucketName,
              Key: filePath   file.name,
              UploadId: multipartUploadId,
            };
            return this.abortMultipartUpload(s3Obj, abortParams);
          }),
          toArray()
        );
      }
    ),
    retry(3), // Needed for createMultipartUpload, which fails on first try sometimes
    concatMap((ETagMap: S3.Parts) => {
      console.log('err', ETagMap);

      const s3CompleteMultipartUploadParams: S3.CompleteMultipartUploadRequest = {
        Bucket: bucketName,
        Key: filePath   file.name,
        UploadId: multipartUploadId,
        MultipartUpload: {
          Parts: ETagMap,
        },
      };
      return from(
        s3Obj
          .completeMultipartUpload(s3CompleteMultipartUploadParams)
          .promise()
      );
    })
  )