Return file from REST webservice

It is easy serving a static asset from a spring web application but what if you needed to fetch a file from a database or a file system? In this episode find out how to return a file from a spring controller.

Detailed Video Notes

In this example we will show how to read a PDF from the classpath simulating a read from a database or a file system and downloading it from a RESTful web service. While we use a PDF you could substitute just about any file format such as gif, jpeg, tiff, png, zip, jars, wars, ear or any other binary format that you need to stream to your browser. More examples show how to make a get request for json and return xml from a spring rest service

Project set up

[0:16]

Visiting spring initializer website and selecting web project dependency we will create a spring boot skeleton project. Opening the project in eclipse we will copy a sample pdf named pdf-sample.pdf into the resource directory. Finally creating a @RestController named DownloadPDFController we will stub in the method in which we will want to map a request to.

@RestController
public class DownloadPDFController {

}

Creating a Request Method

[0:17]

In the DownloadPDFController we will create a default request mapping for the application. The return type will be a ResponseEntity, an extension of HttpEntity that adds a HttpStatus code, and will be of type InputStreamResource. Next for demonstration purposes we fetch a file named pdf-sample.pdf from the classpath by using ClassPathResource. This could be substituted by reading a file from a network drive or retrieving it from a database. Once we have hold of the file we can use ResponseEntityBuilder introduced in Spring 4.1 setting the length, content type and the body of the response to be the file.

Let's go ahead and run our example and navigate to http://localhost:8080/ to view the pdf.

@RequestMapping(value = "/", method = RequestMethod.GET, produces = "application/pdf")
public ResponseEntity<InputStreamResource> downloadPDFFile()
        throws IOException {

    ClassPathResource pdfFile = new ClassPathResource("pdf-sample.pdf");

    return ResponseEntity
            .ok()
            .contentLength(pdfFile.contentLength())
            .contentType(
                    MediaType.parseMediaType("application/octet-stream"))
            .body(new InputStreamResource(pdfFile.getInputStream()));
}

Prevent caching

[1:20]

Often times when you are returning document you may want to prevent caching so that the next time a request is made it returns the most current version. Luckily the ResoponseEnity allows you to set the headers on the response. We will create HttpHeaders object setting Cache-Control, Pragma and Expires properties. In the event where you want to prevent caching across your entire application you could set the response in either a Filter or a HandlerInterceptor.

@RequestMapping(value = "/", method = RequestMethod.GET, produces = "application/pdf")
public ResponseEntity<InputStreamResource> downloadPDFFile()
        throws IOException {

    ClassPathResource pdfFile = new ClassPathResource("pdf-sample.pdf");

    HttpHeaders headers = new HttpHeaders();
    headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
    headers.add("Pragma", "no-cache");
    headers.add("Expires", "0");
    
    return ResponseEntity
            .ok()
            .headers(headers)
            .contentLength(pdfFile.contentLength())
            .contentType(MediaType.parseMediaType("application/octet-stream"))
            .body(new InputStreamResource(pdfFile.getInputStream()));
}

ByteArrayHttpMessageConverter

Just in case you need it, you may need to register a custom message converter named ByteArrayHttpMessageConverter. To do this, if you are using spring boot you can create a bean named "customConverters" creating ByteArrayHttpMessageConverter and populating a HttpMessageConverters object.

@SpringBootApplication
public class ReturnFileFromSpringRestWebserviceApplication {

    public static void main(String[] args) {
        SpringApplication.run(
                ReturnFileFromSpringRestWebserviceApplication.class, args);
    }

    @Bean
    public HttpMessageConverters customConverters() {
        ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
        return new HttpMessageConverters(arrayHttpMessageConverter);
    }
}

Thanks for joining in today's level up, have a great day!