如何使用RestTemplate转发大文件?

Gab*_*abi 36 java groovy spring resttemplate

我有一个Web服务调用,通过它可以上传zip文件.然后将文件转发到另一个服务进行存储,解压缩等.现在文件存储在文件系统中,然后构建FileSystemResource.

Resource zipFile = new FileSystemResource(tempFile.getAbsolutePath());
Run Code Online (Sandbox Code Playgroud)

我可以使用ByteStreamResource以节省时间(在转发之前不需要在磁盘上保存文件)但是为此我需要构建一个字节数组.如果是大文件,我将收到"OutOfMemory:java heap space"错误.

ByteArrayResource r = new ByteArrayResource(inputStream.getBytes());
Run Code Online (Sandbox Code Playgroud)

任何使用RestTemplate转发文件而不会出现OutOfMemory错误的解决方案?

art*_*tol 39

您可以使用execute这种低级操作.在这个片段中,我使用了Commons IO的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);
Run Code Online (Sandbox Code Playgroud)

(感谢Baz指出你需要打电话setBufferRequestBody(false)或它会打败这一点)

  • 啊... autoexec.bat.谢谢给的回忆 (7认同)
  • 找到了一种解决方案,使用*ClientHttpRequestFactory #setBufferRequestBody(false)依次生成一个*StreamingClientHttpRequest,它在调用getBody()时打开连接. (4认同)
  • @Bax不,它会在复制过程中逐个加载.应该是非常节省内存的. (2认同)

Run*_*ror 18

我认为上面的答案有不必要的代码 - 你不需要创建一个匿名的RequestCallback内部类,你不需要使用apache中的IOUtils.

我花了一些时间研究一个类似的解决方案,这就是我提出的:

通过使用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);
Run Code Online (Sandbox Code Playgroud)

(这个例子假设你要POST的地方的响应是JSON.但是,这可以通过改变返回类型类...上面的Map.class来轻松改变)


Ed *_*nin 15

你真正需要的@ artbristol 答案的唯一部分就是这个(你可以设置为RestTemplateSpring bean):

final RestTemplate restTemplate = new RestTemplate();
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);     
restTemplate.setRequestFactory(requestFactory);     
Run Code Online (Sandbox Code Playgroud)

在那之后,我认为只使用一个FileSystemResource作为你的请求主体将做正确的事情.

我已经InputStreamResource成功地使用了这种方式,因为您已经拥有数据InputStream并且不需要多次使用它.

在我的情况下,我们已经压缩了我们的文件并包装了GZipInputStream一个InputStreamResource.