POST InputStream с RestTemplate

У меня есть клиент, которому нужно отправить на сервер большое количество больших файлов json. Мне удалось заставить его работать, прочитав каждый из файлов в память и опубликовав весь файл с помощью RestTemplate. Однако клиенту быстро не хватает памяти при работе с большими файлами json. Я хочу переключиться на потоковый подход, но не могу понять, как правильно использовать FileInputStream с RestTemplate. Я нашел этот вопрос и использовал код, указанный в принятом ответе, но Я все еще вижу использование памяти и исключения OutOfMemory, которые заставляют меня поверить, что файлы не передаются в потоковом режиме, а все еще полностью считываются в память. Что я делаю неправильно? Вот что у меня есть на данный момент:

final InputStream fis = ApplicationStore.class.getResourceAsStream(path);

final RequestCallback requestCallback = new RequestCallback() {
    @Override
    public void doWithRequest(final ClientHttpRequest request) throws IOException {
        request.getHeaders().add("Content-type", "application/json");
        IOUtils.copy(fis, request.getBody());
    }
};

final RestTemplate restTemplate = new RestTemplate();
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);     
restTemplate.setRequestFactory(requestFactory);     
final HttpMessageConverterExtractor<String> responseExtractor =
         new HttpMessageConverterExtractor<String>(String.class, restTemplate.getMessageConverters());

restTemplate.execute("http://" + host + ":8080/upads-data-fabric" + "/ruleset", httpMethod, requestCallback, responseExtractor);

person Tom    schedule 02.12.2015    source источник


Ответы (2)



В дополнение к ответу @sotirios-delimanolis вам также необходимо указать этот параметр для вашего RestTemplate, чтобы внутри ваш org.springframework.http.HttpOutputMessage распознавался как org.springframework.http.StreamingHttpOutputMessage, потому что в противном случае он просто копирует весь поток в свой внутренний поток, поэтому вы просто загружаете его в память. Таким образом, он использует фрагменты исходного потока и отправляет их.

HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate.setRequestFactory(requestFactory);

Я говорю это потому, что существует только одна реализация StreamingHttpOutputMessage, а HttpComponentsClientHttpRequestFactory — единственное место, где она создается.

Воспроизводимый пример:

MultiValueMap<String, Object> bodyMap = new LinkedMultiValueMap<>();
UrlResource urlResource = new UrlResource(MY_EXTERNAL_FILE_URL) { //uses URL#inputStream
    @Override
    public String getFilename() {
        return FILE_NAME;
    }
};
bodyMap.add("file", urlResource); //other service uses -- @RequestParam("file") MultipartFile -- in its controller
RequestEntity<MultiValueMap<String, Object>> request =
    RequestEntity.post(URI.create("http://localhost:6666/api/file"))
        .contentType(MediaType.MULTIPART_FORM_DATA)
        .body(bodyMap);

//should be a @Bean
RestTemplate restTemplate = new RestTemplate ();
HttpComponentsClientHttpRequestFactory requestFactory = new 
HttpComponentsClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate.setRequestFactory(requestFactory);

System.out.println(restTemplate.exchange(request, FileMetadata.class));
person Sam    schedule 26.07.2018
comment
Спасибо. HttpComponentsClientHttpRequestFactory при добавлении в stackoverflow.com/a/52005690/1981358 кажется именно тем, что мне нужно. - person peater; 06.11.2019