POST 带有 RestTemplate 的 InputStream
POST InputStream with RestTemplate
我有一个客户端需要 POST 大量 json 大文件到服务器。我已经能够通过将每个文件读入内存并使用 RestTemplate 发布整个文件来让它工作。但是,客户端在处理 json 大文件时很快就会耗尽内存。我想切换到流式处理方法,但无法弄清楚如何正确地将 FileInputStream 与 RestTemplate 一起使用。我找到 this question 并使用了接受的答案中给出的代码,但我仍然看到内存使用和 OutOfMemory 异常,这让我相信它不是流式传输文件,而是仍然将它们完全读入内存。我究竟做错了什么?这是我目前拥有的:
final InputStream fis = ApplicationStore.class.getResourceAsStream(path);
final RequestCallback requestCallback = new RequestCallback() {
@Override
public void doWithRequest(final ClientHttpRequest request) throws IOException {
request.getHeaders().add("Content-type", "application/json");
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://" + host + ":8080/upads-data-fabric" + "/ruleset", httpMethod, requestCallback, responseExtractor);
不要。使用 Resource
in combination with an appropriate RestTemplate#exchange
方法。
用 Resource
作为 body
创建一个 HttpEntity
。有 ClassPathResource
表示 class 路径资源。 RestTemplate
默认注册一个 ResourceHttpMessageConverter
.
在内部,ResourceHttpMessageConverter
将请求内容流式传输到当前设置为 4096 的 StreamUtils#copy(InputStream, OutputStream)
with a buffer size 连接的另一端。
除了@sotirios-delimanolis 的回答之外,您还需要为您的 RestTemplate
指定此设置,以便在内部将您的 org.springframework.http.HttpOutputMessage
识别为 org.springframework.http.StreamingHttpOutputMessage
,否则它只会复制整个流到它的内部流,所以你只需将它加载到内存中。这样它会使用原始流的块并发送它们。
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate.setRequestFactory(requestFactory);
我这么说是因为 StreamingHttpOutputMessage
只有一个实现,而 HttpComponentsClientHttpRequestFactory
是唯一创建它的地方。
可重现的例子:
MultiValueMap<String, Object> bodyMap = new LinkedMultiValueMap<>();
UrlResource urlResource = new UrlResource(MY_EXTERNAL_FILE_URL) { //uses URL#inputStream
@Override
public String getFilename() {
return FILE_NAME;
}
};
bodyMap.add("file", urlResource); //other service uses -- @RequestParam("file") MultipartFile -- in its controller
RequestEntity<MultiValueMap<String, Object>> request =
RequestEntity.post(URI.create("http://localhost:6666/api/file"))
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(bodyMap);
//should be a @Bean
RestTemplate restTemplate = new RestTemplate ();
HttpComponentsClientHttpRequestFactory requestFactory = new
HttpComponentsClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate.setRequestFactory(requestFactory);
System.out.println(restTemplate.exchange(request, FileMetadata.class));
我有一个客户端需要 POST 大量 json 大文件到服务器。我已经能够通过将每个文件读入内存并使用 RestTemplate 发布整个文件来让它工作。但是,客户端在处理 json 大文件时很快就会耗尽内存。我想切换到流式处理方法,但无法弄清楚如何正确地将 FileInputStream 与 RestTemplate 一起使用。我找到 this question 并使用了接受的答案中给出的代码,但我仍然看到内存使用和 OutOfMemory 异常,这让我相信它不是流式传输文件,而是仍然将它们完全读入内存。我究竟做错了什么?这是我目前拥有的:
final InputStream fis = ApplicationStore.class.getResourceAsStream(path);
final RequestCallback requestCallback = new RequestCallback() {
@Override
public void doWithRequest(final ClientHttpRequest request) throws IOException {
request.getHeaders().add("Content-type", "application/json");
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://" + host + ":8080/upads-data-fabric" + "/ruleset", httpMethod, requestCallback, responseExtractor);
不要。使用 Resource
in combination with an appropriate RestTemplate#exchange
方法。
用 Resource
作为 body
创建一个 HttpEntity
。有 ClassPathResource
表示 class 路径资源。 RestTemplate
默认注册一个 ResourceHttpMessageConverter
.
在内部,ResourceHttpMessageConverter
将请求内容流式传输到当前设置为 4096 的 StreamUtils#copy(InputStream, OutputStream)
with a buffer size 连接的另一端。
除了@sotirios-delimanolis 的回答之外,您还需要为您的 RestTemplate
指定此设置,以便在内部将您的 org.springframework.http.HttpOutputMessage
识别为 org.springframework.http.StreamingHttpOutputMessage
,否则它只会复制整个流到它的内部流,所以你只需将它加载到内存中。这样它会使用原始流的块并发送它们。
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate.setRequestFactory(requestFactory);
我这么说是因为 StreamingHttpOutputMessage
只有一个实现,而 HttpComponentsClientHttpRequestFactory
是唯一创建它的地方。
可重现的例子:
MultiValueMap<String, Object> bodyMap = new LinkedMultiValueMap<>();
UrlResource urlResource = new UrlResource(MY_EXTERNAL_FILE_URL) { //uses URL#inputStream
@Override
public String getFilename() {
return FILE_NAME;
}
};
bodyMap.add("file", urlResource); //other service uses -- @RequestParam("file") MultipartFile -- in its controller
RequestEntity<MultiValueMap<String, Object>> request =
RequestEntity.post(URI.create("http://localhost:6666/api/file"))
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(bodyMap);
//should be a @Bean
RestTemplate restTemplate = new RestTemplate ();
HttpComponentsClientHttpRequestFactory requestFactory = new
HttpComponentsClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate.setRequestFactory(requestFactory);
System.out.println(restTemplate.exchange(request, FileMetadata.class));