HTTP Status 406 – Not Acceptable [Streaming huge data from backend using spring 4.3.x + Java 8]

Ankur Soni :

I am using Spring mvc 4.3.x, java 8, Tomcat 7

CODE:

@Controller
public class StreamRecordsController {

    @RequestMapping(value = "/streamrecords", method = RequestMethod.GET, consumes = MediaType.ALL_VALUE, 
            produces = "application/octet-stream")
    @ResponseBody
    public ResponseEntity<StreamingResponseBody> export() throws FileNotFoundException {
        File file = new File("C:\\Users\\Ankur\\sample.pdf");
        StreamingResponseBody responseBody = outputStream -> {
            Files.copy(file.toPath(), outputStream);
        };
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=generic_file_name.pdf")
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(responseBody);
    }
}

EXCEPTION:

enter image description here

POSTMAN SNAPSHOT

enter image description here QUESTION:

What am I missing here ?

Ankur Soni :

Finally I was able to sort this out which is now helping me stream huge data from backend to Frontend using Spring 4.3.x as mentioned in my post. Below are the points to guide you to execute the program successfully.

Below procedure is so effective that you can even paginate huge data at the back end (can be hibernate, Mongo-java-driver, cassandra java driver, etc)and keep on streaming the data unless your db operation is complete. In some domains like Manufacturing, Insurance, Logistics etc, you need such utility where end user expects quiet huge data from server in the form of CSV, JSON etc to analyse the raw data.

  1. Add one more annotation @EnableWebMvc above your controller class.

  2. When you add above annotation, the code will break at runtime, you can see in catalina.log, this error : java.lang.NoClassDefFoundError: com/fasterxml/jackson/core/util/DefaultIndenter

  3. To fix this you will need to add below jar dependency in your pom.xml

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.8</version>
    </dependency>
    
  4. Now, add <async-supported>true</async-supported> in web.xml under <servlet> tag as below example,

    <servlet>
        <servlet-name>spring</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
    

Below is the code for both FILE Streaming Download and Data Stream support.

DATA STREAM CODE:

package com.emg.server.controller.rest;

import java.io.IOException;
import java.io.OutputStream;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

@Controller
@EnableWebMvc
public class StreamRecordsController {

    @RequestMapping(value = "/streamrecords")
    @ResponseBody
    public StreamingResponseBody export() {
        return new StreamingResponseBody() {
            @Override
            public void writeTo (OutputStream out) throws IOException {
                for (int i = 0; i < 1000; i++) {
                    out.write((Integer.toString(i) + " - ")
                                        .getBytes());
                    out.flush();
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
    }
}

FILE STREAM CODE:

package com.emg.server.controller.rest;

import java.io.File;
import java.nio.file.Files;

import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

@Controller
@EnableWebMvc
public class StreamRecordsController {

    @RequestMapping(value = "/streamrecords", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
    @ResponseBody
    public ResponseEntity<StreamingResponseBody> export() {
         File file = new File("C:\\Users\\Ankur\\sample.pdf");
            StreamingResponseBody responseBody = outputStream -> {
                Files.copy(file.toPath(), outputStream);
            };
            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=generic_file_name.pdf")
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(responseBody);
        }
}

OUTPUT for both the streaming style

enter image description here

NOTE: I execute both programs individually, deployed and tested it separately.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=93966&siteId=1