8

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
  • 4,019
  • 8
  • 50
  • 94
  • 1
    Why do you have both Jackson 1 and Jackson 2 on the classpath? – Sotirios Delimanolis Nov 23 '15 at 17:08
  • According to this: http://stackoverflow.com/a/26616553/1490144 I should have all those dependencies. Isn't that correct? Removing both dependencies (version 1.9.13) doesn't change the response. – Manu Nov 23 '15 at 17:14
  • 1
    (This wasn't to address your problem.) That post is utter nonsense. You don't need both. Each of them causes Spring to provide an `HttpMessageConverter` that handles JSON. You only need one. – Sotirios Delimanolis Nov 23 '15 at 17:36
  • OK, i make my post a bit more precise. What you say actually makes sense and it was my previous configuration. – Manu Nov 23 '15 at 17:51

1 Answers1

11

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);
}
Babl
  • 7,446
  • 26
  • 37
  • 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. – Manu Nov 23 '15 at 18:17
  • Have you removed``produces`` 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 ... – Babl Nov 23 '15 at 18:22
  • I did remove it. If I return an InputStreamReader, I get a 500 error saying that the input stream has already been read. – Manu Nov 23 '15 at 18:24
  • 1
    So lets try the things which I suggested, remove produces, and if not return the byte[] in reponse entity. – Babl Nov 23 '15 at 18:27
  • 1
    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 Nov 23 '15 at 18:30
  • 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. – Manu Nov 23 '15 at 18:31
  • 2
    Just add ("Content-Disposition", "attachment; filename=somefile.pdf") header – Babl Nov 23 '15 at 18:35
  • @Babl : How you calculated `contentLengthOfStream` ? – Sabir Khan Sep 11 '18 at 10:39
  • @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. – Babl Sep 11 '18 at 10:44