Search code examples
javaspringfilespring-bootmodelandview

Passing file/stream between Spring Boot applications


I'm having an issue with Spring Boot (v. 1.5).

Consider following scenario: User sends a POST request to Spring Boot app (let's call it App_A). Controller in App_A sends a new POST request to another Spring Boot app (be it App_B) that does some magic and returns an Excel file to App_A.

Currently App_B returns file as ModelAndView. Here comes the issue. File cannot be deserialized and returned to user, ofcourse that's not surprising. I've been wondering if there is another way of handling this file transfer? Maybe returning octet-stream? But how to receive one in App_A?

AppA files

@RestController
@RequestMapping("/some-route")
public class AppA_RestController {

    private AppB_Connecor connector;

    @PostMapping(value = "/some-handler")
    public ModelAndView handleUserPostRequest(@RequestBody SomeDTO dto) {
        return connector.fetchFile(dto);
    }
}

public class AppB_Connecor {

    public ModelAndView fetchFile(SomeDTO dto) {
        HttpHeaders headers = jsonType();
        HttpEntity<SomeDTO> entity = new HttpEntity<>(dto, headers);
        RestTemplate rest = jsonRestTemplate(); // Just Jackson JSON-object mapper
        String url = "http://some-website.abc/some-api";
        return rest.postForEntity(url, entity, ModelAndView.class);
    }
}

App_B files

@RestController
@RequestMapping("/")
public class AppB_RestController {

    @PostMapping(value = "some-api")
    public ModelAndView generateExcelFile(@RequestBody SomeDTO dto) {
        // Do stuff...
        // ExcelFile extends AbstractXlsxView of apache.poi
        return new ModelAndView(new ExcelFile(), "modelName", values);
    }
}

Solution

  • I figured my issue out. Link posted in comment by M. Prokhorov pointed my in the right direction. All I had to do was to return ResponseEntity<ByteArrayResource> from AppB. You make invoke REST endpoint from AppA just like any other REST service. In AppA byte[] is what you receive.

    AppA files

    @RestController
    @RequestMapping("/some-route")
    public class AppA_RestController {
    
        private AppB_Connecor connector;
    
        @PostMapping(value = "/some-handler")
        public ResponseEntity<byte[]> handleUserPostRequest(@RequestBody SomeDTO dto) {
            return connector.fetchFile(dto);
        }
    }
    
    public class AppB_Connecor {
    
        public byte[] fetchFile(SomeDTO dto) {
            HttpHeaders headers = jsonType();
            HttpEntity<SomeDTO> entity = new HttpEntity<>(dto, headers);
            RestTemplate rest = jsonRestTemplate(); // Just Jackson JSON-object mapper
            String url = "http://some-website.abc/some-api";
            return rest.postForObject(url, entity, byte[].class);
        }
    }
    

    AppB files

    @RestController
    @RequestMapping("/")
    public class AppB_RestController {
    
        @PostMapping(value = "some-api")
        public ResponseEntity<ByteArrayResource> generateExcelFile(@RequestBody SomeDTO dto) {
            // Do stuff...
            OutputStream os = new ByteArrayOutputStream();
            excelFile.getWorkbook().write(os); // apache.poi workbooks can be written to streams
            ByteArrayResource res = new ByteArrayResource(os.getByteArray());
            return ResponseEntity.ok(res);
        }
    }