Spring REST returning PDF - Response status 406 (not acceptable)

18,905

Basically there is no need to add produces = "application/pdf" in RequestMapping as it seems to try to convert the ResponeBody internally. You can just add MediaType to response headers which is what you need.

@ResponseBody
@RequestMapping(value = "get/pdf/{id}", headers="Accept=*/*", method = RequestMethod.GET)
public ResponseEntity<InputStreamResource> getPdfContractById(@PathVariable("id") Long id){
        // Get the remove file based on the fileaddress
        RemoteFile remotefile = new RemoteFile(id);

        // Set the input stream
        InputStream inputstream = remotefile.getInputStream();
        // asume that it was a PDF file
        HttpHeaders responseHeaders = new HttpHeaders();
        InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
        responseHeaders.setContentLength(contentLengthOfStream);
        responseHeaders.setContentType(MediaType.valueOf("application/pdf"));
        // just in case you need to support browsers
        responseHeaders.put("Content-Disposition", Collections.singletonList("attachment; filename=somefile.pdf"))
        return new ResponseEntity<InputStreamResource> (inputStreamResource,
                                   responseHeaders,
                                   HttpStatus.OK);
}
Share:
18,905
Manu
Author by

Manu

Software!

Updated on June 04, 2022

Comments

  • Manu
    Manu almost 2 years

    I read many questions on SO about this type of issue, but all of them recommend using the correct Jackson version. This is my current situation:

    REST API:

    @RequestMapping(value = "get/pdf/{id}", headers="Accept=*/*", method = RequestMethod.GET, produces = "application/pdf")
        @Override
        public ResponseEntity<InputStream> getPdfContractById(@PathVariable("id") Long id);
    

    Using Accept:*/* produces an error in mapping the request (404 occurs)

    From my pom:

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.4.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.4.1.1</version>
        </dependency>
    

    I also tried to add these two dependencies, but nothing changes:

        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-core-asl</artifactId>
            <version>1.9.13</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>1.9.13</version>
        </dependency>
    

    Response client-side: There was an unexpected error (type=Not Acceptable, status=406).Headers incude:

        Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    Accept-Encoding:gzip, deflate, sdch
    

    What's wrong with it?


    More details

    I am using this code to return the remote PDF file:

        URL url = null;
        try {
            url = new URL(urlStr);
        } catch (MalformedURLException e) {
            e.printStackTrace();
            throw new MyException(e.getMessage());
        }
        InputStream pdfFile = null;
        try {
            pdfFile = url.openStream();
        } catch (IOException e) {
            e.printStackTrace();
            throw new MyException(e.getMessage());
        }
    
        ResponseEntity<InputStream> re = ResponseEntity
                .ok()
                        //     .headers(headers)
                        //     .contentLength(contentLength)
                .contentType(
                        MediaType.parseMediaType("application/pdf"))
                .body(pdfFile);
       return re;
    
  • Manu
    Manu over 8 years
    Thanks for your answer. I update my OP and I added the code I am using. It is very similar, but I cannot use an InputStreamResource (my input stream has already been read). That does not seem to work, though.
  • Babl
    Babl over 8 years
    Have you removedproduces from RequestMapping ? And where is your stream read ? I dont see why you can't use InputStream Reader, but if you can, just try to return an byte[] in reponse, see if it works ...
  • Manu
    Manu over 8 years
    I did remove it. If I return an InputStreamReader, I get a 500 error saying that the input stream has already been read.
  • Babl
    Babl over 8 years
    So lets try the things which I suggested, remove produces, and if not return the byte[] in reponse entity.
  • Manu
    Manu over 8 years
    Great! Returning the byte array solves the problem. Also, I am not using 'produces' in the RequestMapping annotation and there is no need to add Jackson dependencies.
  • Manu
    Manu over 8 years
    The only thing I would like now is that the file should not be opened, but rather download with a name I want to choose.
  • Babl
    Babl over 8 years
    Just add ("Content-Disposition", "attachment; filename=somefile.pdf") header
  • Sabir Khan
    Sabir Khan over 5 years
    @Babl : How you calculated contentLengthOfStream ?
  • Babl
    Babl over 5 years
    @SabirKhan basically you can not calculate the content length of the stream without fully going over it, but in most of the cases, the system by its own has that data. Say someone has uploaded the file, the system can store the content length in some DB and read it from there. Otherwise, you will need to read the full stream to get its length.