Как переслать большие файлы с RestTemplate?

У меня есть вызов веб-службы, с помощью которого можно загрузить zip-файлы. Затем файлы передаются в другой сервис для хранения, распаковки и т. Д. На данный момент файл хранится в файловой системе, затем создается FileSystemResource.

Resource zipFile = new FileSystemResource(tempFile.getAbsolutePath());

Я мог бы использовать ByteStreamResource, чтобы сэкономить время (сохранение файла на диске не требуется перед пересылкой), но для этого мне нужно построить байтовый массив. В случае больших файлов я получу ошибку "OutOfMemory: пространство кучи Java".

ByteArrayResource r = new ByteArrayResource(inputStream.getBytes());

Какие-либо решения для пересылки файлов без получения ошибки OutOfMemory с помощью RestTemplate?

4 ответа

Решение

Ты можешь использовать execute для такого рода операций низкого уровня. В этом фрагменте я использовал Commons IO's copy метод для копирования входного потока. Вам нужно будет настроить HttpMessageConverterExtractor за ответ, который вы ожидаете.

final InputStream fis = new FileInputStream(new File("c:\\autoexec.bat")); // or whatever
final RequestCallback requestCallback = new RequestCallback() {
     @Override
    public void doWithRequest(final ClientHttpRequest request) throws IOException {
        request.getHeaders().add("Content-type", "application/octet-stream");
        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://localhost:4000", HttpMethod.POST, requestCallback, responseExtractor);

(Спасибо Базу за указание, что вам нужно позвонить setBufferRequestBody(false) или это победит суть)

Единственная часть ответа @artbristol, которая вам действительно нужна, - это (которую вы можете настроить как RestTemplate Весенняя фасоль):

final RestTemplate restTemplate = new RestTemplate();
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);     
restTemplate.setRequestFactory(requestFactory);     

После этого, я думаю, просто используя FileSystemResource как ваше тело запроса будет делать правильные вещи.

Я также использовал InputStreamResource таким образом, для случаев, когда у вас уже есть данные как InputStream и не нужно потреблять его несколько раз.

В моем случае мы распаковали наши файлы и завернули GZipInputStream в InputStreamResource,

Я думаю, что приведенный выше ответ содержит ненужный код - вам не нужно создавать анонимный внутренний класс RequestCallback, и вам не нужно использовать IOUtils из apache.

Я потратил немного времени на поиски решения, подобного вашему, и вот что я придумал:

Вы можете достичь своей цели намного проще, используя Spring Resource Interface и RestTemplate.

RestTemplate restTemplate = new RestTemplate();

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

File file = new File("/whatever");

HttpEntity<FileSystemResource> requestEntity = new HttpEntity<>(new FileSystemResource(file));
ResponseEntity e = restTemplate.exchange("http://localhost:4000", HttpMethod.POST, requestEntity, Map.class);

(В этом примере предполагается, что ответ, из которого вы отправляете сообщение, - это JSON. Но это можно легко изменить, изменив класс возвращаемого типа... установите выше Map.class)

Еще одна вещь, на которую следует обратить внимание при больших загрузках, — это наличие Spring Actuator. Это автоматически вводитMetricsRestTemplateCustomizerчто приведет к буферизации вашего запроса, даже если вы установитеsetBufferRequestBody(false)на вашем заводе.

Два способа обойти это, возможно, больше вариантов:

  1. Отключить настройщик глобально -
      @SpringBootApplication(exclude = HttpClientMetricsAutoConfiguration.class)
  1. Отключите только некоторые остальные шаблоны, если вы создаетеrestTemplateс помощьюrestTemplateBuilder.
                  var restTemplate = restTemplateBuilder.requestFactory(() ->
                    {
                        var reqFact = new HttpComponentsClientHttpRequestFactory(HttpClientBuilder.create()//
                                .setDefaultRequestConfig(RequestConfig.custom().build())//
                                .build());
                        reqFact.setBufferRequestBody(false);
                        return reqFact;
                    })//
                    .setBufferRequestBody(false)//
                    //these are to disable any possible interceptor/customizer that might force buffering on the request
                    //like the MetricsRestTemplateCustomizer
                    .interceptors(List.of())//
                    .customizers(List.of())//
                    .build();
Другие вопросы по тегам