java - for - ¿Cómo reenviar archivos grandes con RestTemplate?
resttemplate spring 5 example (3)
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 objetivo mucho más simple mediante el uso de 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á 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)
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?
La única parte de la answer 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
.
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<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 a setBufferRequestBody(false)
o se setBufferRequestBody(false)
el punto)