Загрузите большой файл с сервера, используя шаблон REST Java Spring MVC
у меня есть служба REST, которая отправляет мне большой ISO-файл, в службе REST нет проблем . Теперь я написал веб-приложение, которое вызывает службу rest для получения файла, на стороне клиента (веб-приложения) я получаю исключение из памяти.Ниже приведен мой код
HttpHeaders headers = new HttpHeaders();//1 Line
headers.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM));//2 Line
headers.set("Content-Type","application/json");//3 Line
headers.set("Cookie", "session=abc");//4 Line
HttpEntity statusEntity=new HttpEntity(headers);//5 Line
String uri_status=new String("http://"+ip+":8080/pcap/file?fileName={name}");//6 Line
ResponseEntity<byte[]>resp_status=rt.exchange(uri_status, HttpMethod.GET, statusEntity, byte[].class,"File5.iso");//7 Line
Я получаю исключение из памяти в строке 7, я думаю, мне придется буферизировать и получать по частям ,но не знаю, как я могу получить этот файл с сервера, размер файла составляет от 500 до 700 МБ . Кто-нибудь может помочь .
Стека Исключений:
org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.OutOfMemoryError: Java heap space
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:972)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root cause
java.lang.OutOfMemoryError: Java heap space
java.util.Arrays.copyOf(Arrays.java:3236)
java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:118)
java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153)
org.springframework.util.FileCopyUtils.copy(FileCopyUtils.java:113)
org.springframework.util.FileCopyUtils.copyToByteArray(FileCopyUtils.java:164)
org.springframework.http.converter.ByteArrayHttpMessageConverter.readInternal(ByteArrayHttpMessageConverter.java:58)
org.springframework.http.converter.ByteArrayHttpMessageConverter.readInternal(ByteArrayHttpMessageConverter.java:1)
org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:153)
org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:81)
org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:627)
org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1)
org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:454)
org.springframework.web.client.RestTemplate.execute(RestTemplate.java:409)
org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:385)
com.pcap.webapp.HomeController.getPcapFile(HomeController.java:186)
мой код службы отдыха на стороне сервера, который работает нормально, это
@RequestMapping(value = URIConstansts.GET_FILE, produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE}, method = RequestMethod.GET)
public void getFile(@RequestParam(value="fileName", required=false) String fileName,HttpServletRequest request,HttpServletResponse response) throws IOException{
byte[] reportBytes = null;
File result=new File("/home/arpit/Documents/PCAP/dummyPath/"+fileName);
if(result.exists()){
InputStream inputStream = new FileInputStream("/home/arpit/Documents/PCAP/dummyPath/"+fileName);
String type=result.toURL().openConnection().guessContentTypeFromName(fileName);
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
response.setHeader("Content-Type",type);
reportBytes=new byte[100];//New change
OutputStream os=response.getOutputStream();//New change
int read=0;
while((read=inputStream.read(reportBytes))!=-1){
os.write(reportBytes,0,read);
}
os.flush();
os.close();
}
3 ответов
вот как я это делаю. На основе подсказок из этого весенний выпуск Jira.
RestTemplate restTemplate // = ...;
// Optional Accept header
RequestCallback requestCallback = request -> request.getHeaders()
.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
// Streams the response instead of loading it all in memory
ResponseExtractor<Void> responseExtractor = response -> {
// Here I write the response to a file but do what you like
Path path = Paths.get("some/path");
Files.copy(response.getBody(), path);
return null;
};
restTemplate.execute(URI.create("www.something.com"), HttpMethod.GET, requestCallback, responseExtractor);
из вышеупомянутого вопроса Jira:
обратите внимание, что вы не можете просто вернуть InputStream из экстрактора, потому что к моменту возвращения метода execute базовое соединение и поток уже закрыты.
обновление для Spring 5
Весна 5 представила WebClient
класс, который позволяет асинхронные (например, неблокирующие) http-запросы. От Дока:
по сравнению с RestTemplate, WebClient является:
- неблокирующий, реактивный и поддерживает более высокий параллелизм с меньшим количеством аппаратных ресурсов.
- предоставляет функциональный API, который использует преимущества Java 8 lambdas.
- поддерживает синхронные и асинхронные сценарии.
- поддержка потоковой передачи вверх или вниз с сервера.
это предотвращает загрузку всего запроса в память.
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
RestTemplate rest = new RestTemplate(requestFactory);
для java.ленг.OutOfMemoryError: пространство кучи Java можно решить, добавив больше памяти в JVM:
- Xmxn задает максимальный размер пула распределения памяти в байтах. Это значение должно быть кратно 1024 больше 2 МБ. Добавить буква K или K, чтобы указать, килобайт, или M или M, чтобы указать мегабайт. Значение по умолчанию выбирается во время выполнения на основе системы конфигурация.
для серверных развертываний-Xms и-Xmx часто имеют одинаковое значение. См. эргономику сборщика мусора на http://docs.oracle.com/javase/7/docs/technotes/guides/vm/gc-ergonomics.html
примеры:
- Xmx83886080
- Xmx81920k
- Xmx80m
вероятно, проблема, которую вы имеете, не связана строго с запросом, который вы пытаетесь выполнить (загрузить большой файл), но выделенной для процесса памяти недостаточно.
вы должны использовать вложенный файл, поэтому поток файлов не загружается в память. В этом примере я использую службу rest, реализованную с помощью Apache CXF.
...
import org.apache.cxf.jaxrs.ext.multipart.Attachment;
...
@Override
@Path("/put")
@Consumes("multipart/form-data")
@Produces({ "application/json" })
@POST
public SyncResponseDTO put( List<Attachment> attachments) {
SyncResponseDTO response = new SyncResponseDTO();
try {
for (Attachment attr : attachments) {
log.debug("get input filestream: " + new Date());
InputStream is = attr.getDataHandler().getInputStream();