如何使用RestTemplate转发大文件?

问题描述:

我有一个web服务调用,通过它可以上传zip文件。这些文件然后被转发到另一个服务进行存储,解压缩等。 现在,文件存储在文件系统中,然后构建FileSystemResource。如何使用RestTemplate转发大文件?

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

我可以用一个ByteStreamResource为了节省时间(磁盘上的文件的保存转发之前没有必要),但我需要建立一个字节数组。在大文件的情况下,我会得到一个“OutOfMemory:java堆空间”错误。

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

的任何解决方案,以转发文件没有得到使用RestTemplate的内存不足的错误?

+0

灿你是否将输入流传递给其他服务?或者您必须将输入流写入文件,然后将文件句柄传递给服务。另外,不确定这与Groovy有何关系? –

+0

我没有找到任何方法来传递输入流。我使用Groovy标签,因为代码是在groovy中(java InputStream没有getBytes方法) –

+1

啊,我是在用Java风格编写时抛出的;-)那么这个其他服务接受了什么呢? –

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

(感谢巴兹的指出你需要调用setBufferRequestBody(false)或将击败点)

+0

是不是所有的输入流都以这种方式加载到内存中? – Bax

+2

@Bax不,在复制过程中它会被零碎地装入。应该是相当有记忆效率的。 – artbristol

+0

doWithRequest(request)在request.execute()之前被调用,并且在所有流被复制到请求主体之前它不会返回。 cintent被大块复制到身体上,但没有人从它身上消耗掉,它不像管子那样起作用,我是否错过了smth? – Bax

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

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

在那之后,我觉得只是使用FileSystemResource作为您的要求的身体会做正确的事。

我也用这种方式成功地使用了InputStreamResource,对于已经有数据作为InputStream并且不需要多次使用它的情况。

在我的情况下,我们gzipped我们的文件,并在InputStreamResource包装GZipInputStream

我认为上面的答案有不必要的代码 - 你不需要创建一个匿名的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') 

ResponseEntity e = restTemplate.exchange("http://localhost:4000", HttpMethod.POST, new HttpEntity<Resource>(new FileSystemResource(file)), Map.class); 

(这个例子假定从那里你要发布帖子的回应是JSON。但是,这可以很容易地通过改变返回类型类...设置上面Map.class改变)