Java 如何使用RestTemplate转发大文件?

Java 如何使用RestTemplate转发大文件?,java,spring,groovy,resttemplate,Java,Spring,Groovy,Resttemplate,我有一个web服务调用,通过它可以上传zip文件。然后,这些文件被转发到另一个服务进行存储、解压缩等。 现在,文件存储在文件系统上,然后构建一个FileSystemResource Resource zipFile = new FileSystemResource(tempFile.getAbsolutePath()); 我可以使用ByTestStreamResource来节省时间(在转发之前不需要在磁盘上保存文件),但为此我需要构建一个字节数组。对于大文件,我将得到一个“OutOfMemor

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

Resource zipFile = new FileSystemResource(tempFile.getAbsolutePath());
我可以使用ByTestStreamResource来节省时间(在转发之前不需要在磁盘上保存文件),但为此我需要构建一个字节数组。对于大文件,我将得到一个“OutOfMemory:java堆空间”错误


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

编辑:其他答案更好(使用
资源

我原来的答覆是:

对于这种低级操作,可以使用
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);
final InputStream fis=新文件InputStream(新文件(“c:\\autoexec.bat”);//或者别的什么
final RequestCallback RequestCallback=new RequestCallback(){
@凌驾
public void doWithRequest(最终ClientHttpRequest请求)引发IOException{
request.getHeaders().add(“内容类型”、“应用程序/八位字节流”);
copy(fis,request.getBody());
}
};
最终RestTemplate RestTemplate=新RestTemplate();
SimpleClientHttpRequestFactory requestFactory=新的SimpleClientHttpRequestFactory();
requestFactory.setBufferreRequestBody(false);
setRequestFactory(requestFactory);
最终HttpMessageConverterExtractor响应Extractor=
新的HttpMessageConverterExtractor(String.class,restTemplate.getMessageConverters());
restTemplate.execute(“http://localhost:4000,HttpMethod.POST,requestCallback,responseExtractor);

(感谢Baz指出您需要调用
setBufferreRequestBody(false)
,否则它将无法解决问题)

您真正需要的@artbristol的唯一部分是这个(您可以将其设置为
RestTemplate
Springbean):

在这之后,我认为仅仅使用
文件系统资源作为请求主体将做正确的事情

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


在我的例子中,我们已经gzip了我们的文件,并将一个
gzip输入流
包装在
InputStreamResource

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

我花了一点时间研究了一个类似于您的解决方案,这就是我想到的:

通过使用和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);
RestTemplate RestTemplate=new RestTemplate();
SimpleClientHttpRequestFactory requestFactory=新的SimpleClientHttpRequestFactory();
requestFactory.setBufferreRequestBody(false);
setRequestFactory(requestFactory);
File File=新文件(“/whatever”);
HttpEntity requestEntity=新HttpEntity(新文件系统资源(文件));
ResponseEntity e=restTemplate.exchange(“http://localhost:4000,HttpMethod.POST,requestEntity,Map.class);

(本例假设您发布到的响应是JSON。但是,可以通过更改返回类型class…设置为上面的Map.class来轻松更改)

您不能将inputstream传递给其他服务吗?或者,您必须将inputstream写入文件,然后将文件句柄传递给服务。另外,我不确定这与Groovy有什么关系?我没有找到任何方法只传递输入流。我使用Groovy标记是因为代码在Groovy中(java InputStream没有getBytes方法)啊,我是在您以非常java风格编写它时抛出的;-)那么,这个其他服务接受什么呢?我不提供这个答案,因为它的范围比您的问题的答案要大一些,但是您是否考虑过针对这个问题的Spring集成?您基本上看到的是一种声明检查模式,带有web服务和REST适配器。通过SI框架,您可以完成许多工作。输入流不是这样加载到内存中的吗?@Bax否,复制时会逐段加载。应该非常节省内存。@Bax
doWithRequest
在匿名类中;它以前定义过,但在执行(它是回调)找到某种解决方案之前不会被调用,使用*ClientHttpRequestFactory#setBufferRequestBody(false)依次生成*StreamingClientHttpRequest,在调用getBody()时打开连接。啊。。。autoexec.bat。谢谢你的回忆
final RestTemplate restTemplate = new RestTemplate();
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);     
restTemplate.setRequestFactory(requestFactory);     
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);