3

I have a backend (Spring Boot) API that writes data chunks to the response. I am trying to read that from my Angular code. What I expected was to receive chunks of data from the server. However the get method of httpClient does not return anything till all the data is retrieved from the server. I have given the code below

Spring Boot code

@GetMapping(value = "/stream", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<StreamingResponseBody> fetchData(@RequestParam String path) {
    try {
        Pair<InputStream, Long> streamAndLen = CommonUtils.getTestStream();
        InputStream stream = streamAndLen.getKey();
        StreamingResponseBody resp =  outputStream -> {
            CommonUtils.copy(stream, outputStream);
            stream.close();
        };
        return ResponseEntity.ok()
            .header(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_TYPE)
            .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE)
            .contentLength(streamAndLen.getValue())
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .body(resp);
    } catch (Exception e) {
        throw new RuntimeException("Could not get the data for the requested file", e);
    }
}

==============CommonUtils==================

public static Pair<InputStream, Long> getTestStream() {
    File file = new File("file_path_in_system");
    try {
        return new Pair<>(new FileInputStream(file), file.length());
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        throw new RuntimeException("File " + file.getAbsolutePath() + " is not found.", e);
    }
}

public static void copy(InputStream stream, OutputStream outputStream) {
    try {
        int buffSize = 1024 * 4;
        byte[] data = new byte[buffSize];
        int length = -1;
        while ((length = stream.read(data)) != -1) {
            outputStream.write(data, 0, length);
            outputStream.flush();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

The Pair is just a key and value POJO.

Angular snippet

let headers = new HttpHeaders().set('Accept', 'application/octet-stream');
this.http.get(env.streamURL + '?path=' + this.filePath, { headers, observe: 'body', reportProgress: true, responseType: 'blob' })
  .pipe(
    map(res => {
      console.log(res);
    })
  )
  .subscribe(
    (res: any) => {
      console.log('res = ' + res);
    }
  );

I want to get chunks of data (After every call to flush from the server) in my Angular application rather than waiting for all the chunks. Is there a way it can be achieved?

user1223879
  • 123
  • 3
  • 13

1 Answers1

3

If you want to get chunks from the server, I will suggest HTML 5 API, i.e. SSE

You can read more about it
https://www.w3schools.com/html/html5_serversentevents.asp

Also, you can see the demo in the below link
https://www.w3schools.com/html/tryit.asp?filename=tryhtml5_sse

Vishal Petkar
  • 369
  • 1
  • 4
  • 20
  • 1
    Thanks for your inputs. I am looking at something where I can pass the auth credentials or tokens. When I tried using SSE earlier, I figured out that EventSource doesnt support authentication. – user1223879 Sep 19 '20 at 20:35
  • You can pass an EventSourceInit object to the EventSource constructor, e.g. `new EventSource(url, { withCredentials: true })`. https://stackoverflow.com/questions/16266493/html5-server-sent-events-how-to-set-withcredentials-option – Matt Nov 27 '20 at 20:06
  • Using SSE is a bad idea because browsers restrict them up to max connections around 5 – Yasser Jarouf Jun 05 '21 at 13:01