¿Cómo reenviar archivos grandes con RestTemplate?

Tengo una llamada al servicio web a través de la cual se pueden cargar los archivos zip. A continuación, 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, y luego se genera un FileSystemResource.

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

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

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

¿Alguna solución para reenviar archivos sin obtener un error de OutOfMemory al usar RestTemplate?

Puede usar execute para este tipo de operación de bajo nivel. En este fragmento, utilicé el método de copia de Commons IO para copiar el flujo de entrada. HttpMessageConverterExtractor personalizar 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 responseExtractor = new HttpMessageConverterExtractor(String.class, restTemplate.getMessageConverters()); restTemplate.execute("http://localhost:4000", HttpMethod.POST, requestCallback, responseExtractor); 

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

Creo que la respuesta anterior tiene un código innecesario: no es necesario crear una clase interna RequestCallback anónima, y ​​no es necesario utilizar IOUtils de apache.

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

Puede lograr su objective mucho más simple mediante el uso de la interfaz de recursos de spring y RestTemplate.

 RestTemplate restTemplate = new RestTemplate(); SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); requestFactory.setBufferRequestBody(false); restTemplate.setRequestFactory(requestFactory); File file = new File("/whatever"); HttpEntity 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á realizando la POSTCIÓN es JSON. Sin embargo, esto se puede cambiar fácilmente cambiando la clase de tipo de devolución … establecida en Map.class arriba)

La única parte de la respuesta de @artbristol que realmente necesita es esto (que puede configurar como un bean RestTemplate Spring):

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

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

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

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