0

We have 3 tier System (UI,Server1,Server2)

We have to transfer files from our client (Angular) to some server (java server1, Spring) and this server has to pass the file data to the second server

UI => Server 1 ==> Server 2

We want to stream the bytes from the UI to "Server 2" without storing the files on the disk of "Server 1" or without storing the file data in the memory of "Server 1"

The problem that the link "UI" ==> "Server 1" is fast

and the link from "Server 1" ==> "Server 2" is slow

And we face the problem that the file data gets buffered in the output stream towards "server 2"

This is the function that we currently use:

private ResponseEntity<String> upload(String url, HttpHeaders headers, HttpServletRequest request) {
        // copy bytes from caller input stream to FE request stream
        RequestCallback requestCallback = (ClientHttpRequest req) -> {
            Utils.propagateHeaders(headers,req);
            request.getInputStream().transferTo(req.getBody());
        };

        // execute the call to server 2
        ResponseEntity executeResult = template.execute(url, HttpMethod.PUT, requestCallback, (clientHttpResponse) -> {
            HttpStatus statusCode = clientHttpResponse.getStatusCode();               
            return ResponseEntity.status(statusCode);
        });
        return executeResult;
    }

So we understand that this is an issue of back-pressure ,request.getInputStream() has to send the data to the output stream in the speed that the output stream can send the data over the wire

What is the best way to do that?

Thanks

Maayan Hope
  • 1,482
  • 17
  • 32

1 Answers1

0

Ended up replacing the RestTemplate that was used with WebClient that receives Flux as parameter for the body of the API call

// create a Flux of DataBuffer from the InputStream that we get from UI
   Flux<DataBuffer> dataBufferFlux = DataBufferUtils.readByteChannel(() -> Channels.newChannel(request.getInputStream()), new DefaultDataBufferFactory(), 8192);

   // call PUT API providing the dataBufferFlux as parameter for body
   Mono<ClientResponse> responseMono = webClient.put()
                .uri(url)
                .body(BodyInserters.fromDataBuffers(dataBufferFlux))
                .headers((HttpHeaders head) ->head.addAll(headers))
                .exchange();
 

This change seems to reduce the memory footprint and not all memory was allocated immediately after API was called

This is the dependency I had to add

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Maayan Hope
  • 1,482
  • 17
  • 32