3

I'm trying to download an Excel file using RESTful call. I thought it should be pretty simple, but I keep getting HTTP 406 Not Applicable Eror. This is what my controller method looks like:

@RequestMapping(value = "/gettemplate", method = RequestMethod.GET, produces = "application/vnd.ms-excel")
@ResponseBody
public Response getExcelTemplate() throws Exception {
    File templateFile = new File("TestFile.xlsx");
    ResponseBuilder builder = Response.ok(templateFile,"application/vnd.ms-excel");
    builder.header("Content-Disposition", "attachment; filename=\"" + templateFile.getName() + "\"" );
    return builder.build();
}

I've tried setting request header to accept application/vnd.ms-excel, I've also tried using application/octet-stream instead of vnd.ms-excel. I get an HTML response back with 406 error message in either case. Here's what my Ajax test call looks like:

Ext.Ajax.request({
    url: 'myservice/gettemplate',
    //dataType: 'application/vnd.ms-excel',
    headers: {
        'Accept': 'application/vnd.ms-excel, text/plain, */*'
        //'Content-Type': 'application/vnd.ms-excel'
    },
    success: function (response, options) {
        alert(1);
    },
    failure: function (response, options) {
        alert(2);
    }
});

I've commented out the lines that I've tried and removed as it didn't help. This could be a very simple config change, but I can't seem to figure out.

Roman C
  • 49,761
  • 33
  • 66
  • 176
Suraj Bajaj
  • 6,630
  • 5
  • 34
  • 49
  • The content type is being declared twice, once in the `produces` value in the @RequestMapping annotation and another one on the ResponseBuilder. Try removing one of them. – ESala Oct 20 '15 at 19:04
  • @ESala Removing one doesn't help. Tried changing to `ResponseBuilder builder = Response.ok(templateFile);` still get the same error. – Suraj Bajaj Oct 20 '15 at 19:13

2 Answers2

4

When you annotate a request mapping return type with @ResponseBody, Spring will attempt to convert the object into the response using an HttpMessageConverter, as it says on the reference page:

@ResponseBody As with @RequestBody, Spring converts the returned object to a response body by using an HttpMessageConverter.

You can see the list of available converters here: Message converters

It looks like the application/vnd.ms-excel you specify is not supported by any of the converters. Maybe that is why you get a 406.

406 Not Acceptable The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request.

The solution in your case is to remove the @ResponseBody annotation and handle the file download in another way.

See here for examples on how to download a file from a Spring controller:

Downloading a file from Spring controllers

Returning a file from a controller in Spring

Community
  • 1
  • 1
ESala
  • 6,878
  • 4
  • 34
  • 55
  • 1
    I have the same problem with `application/octet-stream` as well, it is there on the list of Message Converters you mentioned. – Suraj Bajaj Oct 20 '15 at 21:00
1

Somewhat based on ESala's answer I tried a few things. Finally got it working by setting header and content type to HttpServletResponse. Here's the code that worked for me. This works in both IE and Firefox flawlessly with just a simple anchor tag, without even an ajax call. Hope it helps someone else.

@RequestMapping(value ="/gettemplate", method = RequestMethod.GET)
@ResponseBody
public void getExcelTemplate(HttpServletRequest request, HttpServletResponse response) throws Exception{
    response.setContentType("application/vnd.ms-excel");
    response.setHeader("content-disposition", "attachment; filename=TestFile.xlsx");

    InputStream fis = getClass().getClassLoader().getResourceAsStream("templates/TestFile.xlsx");
    int x = fis.available();
    byte byteArray[] = new byte[x];
    logger.info(" File size :"+byteArray.length);
    fis.read(byteArray);

    response.getOutputStream().write(byteArray);
    response.flushBuffer();
    fis.close();
}
Suraj Bajaj
  • 6,630
  • 5
  • 34
  • 49