如何使用RestTemplate转发大文件?

时间:2013-04-03 08:00:07

标签: java spring groovy resttemplate

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

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

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

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

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

3 个答案:

答案 0 :(得分:37)

您可以使用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);

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

答案 1 :(得分:15)

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

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

使用the 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);

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

答案 2 :(得分:13)

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

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

之后,我认为只需使用FileSystemResource作为您的请求主体即可。

我也已成功使用InputStreamResource这种情况,因为您已将数据作为InputStream并且不需要多次使用它。

在我的情况下,我们已经压缩了我们的文件并将GZipInputStream包裹在InputStreamResource中。