Search code examples
javaspringspring-bootresttemplateresponsestream

Rest template read response as stream and pass to spring controller(Get InputStream with rest Template)


I have a url which download the large size zip file.It returns the response as stream.though file size is large first it returns 200(HTTPSTATUK.OK) and continues download.

I have to implement a new spring controller which call the above url through rest template.I have to read the response returned by rest template and pass to controller.initially I have implemented in below way

     @GetMapping("/export/downloadFile")
        public ResponseEntity<byte[]> downloadData(Model model,
                                                       @ModelAttribute(EXCEPTION_COLLECTOR) ExceptionCollector exceptionCollector,
                                                       @RequestParam("userName") String userName,
                                                       @RequestParam("startDate") Date startDate,
                                                       @RequestParam("endDate") Date endDate,
                                                       @RequestParam("reason") String reason) {

   URI uri = /building url here/;
    
    return restTemplate.exchange(uri, HttpMethod.GET, new HttpEntity<>(httpHeaders), byte[].class);
    
    }

since I am using ResponseEntity<byte[]> , rest template waits till entire file loaded into memory.so very frequently I am getting socket timeout issue.

Do we have way to read the response as stream and return to controller.

I found few things about restTemplate.execute .

restTemplate.execute(uri,HttpMethod.GET,requestCallBack,clientHttpResponse -> {
                File ret = File.createTempFile("download", ".zip",new File("/Users/bokkavijay/Desktop"));
                StreamUtils.copy(clientHttpResponse.getBody(), new FileOutputStream(ret));
                return ret;
            });

above snippet can copy the file to our local with out time out but this is not what I need.

how can we pipe the stream in clientHttpResponse to controller ?


Solution

  • I found the working implementation

    Controller

        @GetMapping("/readResponseAsStream")
            public ResponseEntity<StreamingResponseBody> downloadAsStream(Model model,HttpServletResponse response) {
    
                 HttpHeaders httpHeaders=new HttpHeaders();
                 httpHeaders.add("Transfer-Encoding","chunked");
                 httpHeaders.add("Content-Type","x-zip-compressed");
                 httpHeaders.add("Content-Disposition", "attachment;filename=sample.zip");
    
                ServletOutputStream servletOutputStream=response.getOutputStream();
    
                StreamingResponseBody downloadFile = out -> {
                     
    
                          RequestCallback requestCallBack=request->{
                                request.getHeaders().add(//Add headers here);
                          };
    
                          ResponseExtractor<ServletOutputStream> responseExtractor = clientHttpResponse -> {
    
                                  //code snippet if you want to write response stream to HttpServletResponse
                                  byte[] buff = new byte[800000];
                                  int bytesRead = 0;
                                      while ((bytesRead = clientHttpResponse.getBody().read(buff)) != -1) {
                                        servletOutputStream.write(buff, 0, bytesRead);
                                        }
                                  return servletOutputStream;
    
    
                                  //Incase if you want to copy file to you local
                                   File ret = File.createTempFile("download", ".zip",new File("Add Local system directory address here"));
                                   StreamUtils.copy(clientHttpResponse.getBody(), new FileOutputStream(ret));
    
                                   //You can copy the the clientHttpResponse.getBody() to ByteArrayInputStream and return
                                   // Don't return clientHttpResponse.getBody() directly because rest template will close the inputStream(clientHttpResponse.getBody()) after .execute completed
    
                                   //if you want to write restTemplate.execute in dao layer , pass servletOutputStream as a argument to method
    
                                  };
    
                       restTemplate.execute(URL_ADDRESS,HttpMethod.GET,requestCallBack,responseExtractor);
    
    
                };
    
    
                return new ResponseEntity(downloadFile,httpHeaders,HttpStatus.OK);
    }
    

    If you write the response directly to HttpServletResponse , controller download the file when we access in browser