¿Cómo reenviar archivos grandes con RestTemplate?


Tengo una llamada de servicio web a través de la cual se pueden cargar archivos zip. Los archivos se reenvían a otro servicio para su almacenamiento, descompresión, etc. Por ahora el archivo se almacena en el sistema de archivos, luego se construye un FileSystemResource.

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

Podría usar un ByteStreamResource para ahorrar tiempo(no es necesario guardar el archivo en el disco antes de reenviar), pero para eso necesito construir una matriz de bytes. En caso de archivos grandes obtendré un " OutOfMemory: java heap space" error.

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

¿Alguna solución para reenviar archivos sin obtener un error OutOfMemory usando RestTemplate?

Author: Gabriela Visinari, 2013-04-03

3 answers

Puede usar execute para este tipo de operación de bajo nivel. En este fragmento he usado el método copy de Commons IO para copiar el flujo de entrada. Tendría que personalizar el HttpMessageConverterExtractor para el tipo de respuesta que está esperando.

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);

(Gracias a Baz por señalar que necesita llamar setBufferRequestBody(false) o derrotará el punto)

 34
Author: artbristol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-16 12:23:36

Creo que la respuesta anterior tiene código innecesario - no necesita hacer una clase interna RequestCallback anónima, y no necesita usar IOUtils de apache.

Pasé un poco de tiempo investigando una solución similar a la suya y esto es lo que se me ocurrió:

Puede lograr su objetivo mucho más simple utilizando la Interfaz de Recursos de primavera y 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);

(Este ejemplo asume que la respuesta desde donde está publicando es JSON. Pero, esto se puede cambiar fácilmente cambiando la clase de tipo de retorno... set a Mapa.clase anterior)

 13
Author: RuntimeBlairror,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-06 18:24:49

La única parte de la respuesta de @artbristol que realmente necesitas es esta (que puedes configurar como un RestTemplate frijol de primavera):

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

Después de eso, creo que solo usar un FileSystemResource como su cuerpo de solicitud hará lo correcto.

También he usado un InputStreamResource con éxito de esta manera, para casos en los que ya tiene los datos como un InputStream y no necesita consumirlos varias veces.

En mi caso, habíamos descomprimido nuestros archivos y envuelto un GZipInputStream en un InputStreamResource.

 12
Author: Ed Brannin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:41