Как переслать большие файлы с 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)
на вашем заводе.
Два способа обойти это, возможно, больше вариантов:
- Отключить настройщик глобально -
@SpringBootApplication(exclude = HttpClientMetricsAutoConfiguration.class)
- Отключите только некоторые остальные шаблоны, если вы создаете
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();