es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Observable en un bucle for?

Me gustaría llamar a un servicio REST observable en un bucle for. Retorna un archivo CSV codificado en base64. Me gustaría decodificarlo y concatenarlo en una sola cadena y retornarlo. Después de eso, intento suscribirme a ese método y hacer clic en el DOM para descargarlo. Obtengo una cadena vacía con solo "\r\n". ¿Por qué no espera a que el REST retorne el archivo antes de devolverlo?

downloadFilesAndConcatenate(): Observable<any> {
    let concatenatedFileDecoded: string = '\r\n';
    for (let i = 0; i < this.fileIDs.length; i++) {
      this.restService.getFile(this.fileIDs[i]).subscribe(response => {
        this.fileResultSet = response;
        this.message = response.message;
        this.file = this.fileResultSet.result;
        let fileCSVbase64 = this.file.fileBytes
        let fileCSVDecoded = atob(fileCSVbase64);
        concatenatedFileDecoded += fileCSVDecoded;
      },
      error => {
        this.message = error.error.message;
      });

      return new Observable( observer => {
        observer.next(concatenatedFileDecoded)
        observer.complete();
      });
    }
  }

Y luego me suscribo a él:

descargar() {
    if (this.dateEnd !== null && typeof this.dateEnd !== "undefined") {
      debugger;
      this.downloadFilesAndConcatenate()  // Múltiples archivos
        .subscribe( 
          (result) => {
            debugger;
            const link = document.createElement( 'a' );
            link.style.display = 'none';
            document.body.appendChild( link );

            const blob = new Blob([result], {type: 'text/csv'});
            const objectURL = URL.createObjectURL(blob); 

            link.href = objectURL;
            link.href = URL.createObjectURL(blob);
            link.download =  this.file.name;
            link.click();
          },
          (err) => {
            console.error(err);
          },
          () => console.log("descarga observable completa")
        );
    } else {
      this.downloadFile();  // Solo un archivo
    }
  }
Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    El problema es que la función downloadFilesAndConcatenate() no espera a que todas las llamadas al servicio REST se completen antes de devolver el archivo concatenado. La instrucción return new Observable está dentro del bucle for y se llama después de la primera llamada al servicio REST, por lo que solo devuelve un observable con el archivo concatenado de la primera llamada.

    Para solucionar esto, puedes utilizar el operador forkJoin de la librería rxjs para combinar todas las llamadas al servicio REST y esperar a que se completen antes de devolver el archivo concatenado. Así es como puedes modificar la función downloadFilesAndConcatenate():

    downloadFilesAndConcatenate(): Observable<string> {
      const fileObservables: Observable<string>[] = [];
      for (let i = 0; i < this.fileIDs.length; i++) {
        fileObservables.push(this.restService.getFile(this.fileIDs[i]).pipe(
          map(response => {
            const fileCSVbase64 = response.result.fileBytes;
            return atob(fileCSVbase64);
          })
        ));
      }
      return forkJoin(fileObservables).pipe(
        map(files => files.join('\r\n'))
      );
    }
    

    Este código crea un array de observables que llaman al servicio REST para cada archivo, decodifica el archivo base64 en una cadena y luego utiliza el operador forkJoin para combinar todos los observables en un único observable que emite un array de cadenas de archivo. Finalmente, se utiliza el operador map para concatenar todas las cadenas de archivo en una única cadena separada por '\r\n'.

    La función download() debería funcionar como antes, pero ahora recibirá la cadena de archivo concatenada desde el observable devuelto por downloadFilesAndConcatenate().

Comments are closed.