0

I would like to call an observable REST service in a for loop. It returns base64 encoded csv file. I would like to decode it and concatenate it into one string and return it. After that I am trying to subsrciibe to that method and click the DOM to download. I get empty string with only "\r\n" in it. Why doesn't it wait for REST to return the file before returning?

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();
      });
    }
  }

And then I subscribe to it:

download() {
    if (this.dateEnd !== null && typeof this.dateEnd !== "undefined") {
      debugger;
      this.downloadFilesAndConcatenate()  // Multiple files
        .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("download observable complete")
        );
    } else {
      this.downloadFile();  // Only one file
    }
  }

1 Answer 1

3

because it is asynchroniuos code. it is expected that sync code will be executed earlier than async. correct code would be like this

downloadFilesAndConcatenate(): Observable<string> {
    return forkJoin(this.fileIDs.map(id => this.restService.getFile(id))).pipe(
      map(responses => '\r\n'+responses.map(r => atob(r.result.fileBytes)).join(''))
      catchError(e => this.message = e.error.message)
    );
  }
Sign up to request clarification or add additional context in comments.

4 Comments

Implemented catchError and imported forkJoin from RxJS. However, for second map (in pipe) I get Cannot find name 'map'. Did you mean 'Map'?ts(2552) lib.es2015.collection.d.ts(36, 13): 'Map' is declared here. If I do that, then I get: Value of type 'MapConstructor' is not callable. Did you mean to include 'new'?
it should be also imported from rxjs as well as catchError
Tnx, imported from here import { map, catchError } from 'rxjs/operators';
All files are downloaded and joined in one csv. It's working, thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.