use*_*349 28 java inputstream resttemplate
我正在使用URL类从中读取InputStream.有什么方法可以使用RestTemplate吗?
InputStream input = new URL(url).openStream();
JsonReader reader = new JsonReader(new InputStreamReader(input, StandardCharsets.UTF_8.displayName()));
Run Code Online (Sandbox Code Playgroud)
我如何InputStream 使用RestTemplate而不是使用URL?
Abh*_*kar 33
以前的答案并没有错,但它们并没有深入到我喜欢的深度.有些情况下处理低级别InputStream不仅是可取的,而且是必要的,最常见的例子是将大型文件从源(某些Web服务器)流式传输到目标(数据库).如果您尝试使用a ByteArrayInputStream,那么您将会受到欢迎,并不会令人惊讶OutOfMemoryError.是的,您可以滚动自己的HTTP客户端代码,但是您必须处理错误的响应代码,响应转换器等.如果您已经在使用Spring,那么寻找RestTemplate是一个很自然的选择.
在撰写本文时,spring-web:5.0.2.RELEASE有一个ResourceHttpMessageConverter具有a boolean supportsReadStreaming,如果设置,并且响应类型为InputStreamResource,则返回InputStreamResource; 否则它返回一个ByteArrayResource.很明显,你并不是唯一一个要求流媒体支持的人.
但是,有一个问题:RestTemplate在HttpMessageConverter运行后很快关闭响应.因此,即使您要求InputStreamResource并获得它也没有用,因为响应流已经关闭.我认为这是一个他们忽略的设计缺陷; 它应该依赖于响应类型.所以不幸的是,对于阅读,你必须完全消费反应; 如果使用你就无法传递它RestTemplate.
写作没有问题.如果你想流式传输InputStream,ResourceHttpMessageConverter将为你做.在内部,它使用org.springframework.util.StreamUtils在从一个时间写4096个字节InputStream到OutputStream.
一些HttpMessageConverter支持所有媒体类型,因此根据您的要求,您可能必须从中删除默认类型RestTemplate,并设置您需要的那些,注意它们的相对排序.
最后但并非最不重要,实现ClientHttpRequestFactory有boolean bufferRequestBody,你可以,也应该设置为false你上载大量流.否则,你知道,OutOfMemoryError.在撰写本文时,SimpleClientHttpRequestFactory(JDK客户端)和HttpComponentsClientHttpRequestFactory(Apache HTTP客户端)支持此功能,但不支持OkHttp3ClientHttpRequestFactory.再次,设计监督.
编辑:提交的票据SPR-16885.
Abd*_*ull 25
春天有一个org.springframework.http.converter.ResourceHttpMessageConverter.它转换了Spring的org.springframework.core.io.Resource课程.那Resource类封装InputStream,您可以通过获得someResource.getInputStream().
把这个都在一起,其实你可以得到一个InputStream通过RestTemplate外的开箱通过指定Resource.class为您RestTemplate调用的响应类型.
下面是使用的一个示例RestTemplate的exchange(..)方法:
import org.springframework.web.client.RestTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.core.io.Resource;
ResponseEntity<Resource> responseEntity = restTemplate.exchange( someUrlString, HttpMethod.GET, someHttpEntity, Resource.class );
InputStream responseInputStream;
try {
responseInputStream = responseEntity.getBody().getInputStream();
}
catch (IOException e) {
throw new RuntimeException(e);
}
// use responseInputStream
Run Code Online (Sandbox Code Playgroud)
我遇到了同样的问题,并通过扩展 RestTemplate 并仅在读取流后关闭连接来解决它。
你可以在这里看到代码:https : //github.com/ItamarBenjamin/stream-rest-template
您不应该InputStream直接获得。RestTemplate旨在封装处理响应(和请求)内容。它的优势在于处理所有IO,并为您提供现成的Java对象。
您需要注册适当的HttpMessageConverter对象。这些对象将可以InputStream通过HttpInputMessage对象访问响应的。
作为Abdull表明,Spring并配备了HttpMessageConverter用于实现Resource其自身的包装InputStream,ResourceHttpMessageConverter。它不支持所有Resource类型,但是由于无论如何都应该对接口进行编程,因此应该只使用superinterface Resource。
当前的实现(4.3.5)将返回ByteArrayResource,并将响应流的内容复制到ByteArrayInputStream可以访问的新内容。
您不必关闭流。在RestTemplate你需要照顾那个。(这很不幸,如果您尝试使用InputStreamResource,则支持的另一种类型ResourceHttpMessageConverter,因为它包装了底层的响应,InputStream但是在可以公开给客户代码之前已被关闭。)
非常简单但有效的解决方案是使用ResponseExtractor. 当您想要对非常大的输入流进行操作并且您的 RAM 有限时,它特别有用。
以下是您应该如何实施它:
public void consumerInputStreamWithoutBuffering(String url, Consumer<InputStream> streamConsumer) throws IOException {
final ResponseExtractor responseExtractor =
(ClientHttpResponse clientHttpResponse) -> {
streamConsumer.accept(clientHttpResponse.getBody());
return null;
};
restTemplate.execute(url, HttpMethod.GET, null, responseExtractor);
}
Run Code Online (Sandbox Code Playgroud)
然后,在您需要的任何地方调用该方法:
Consumer<InputStream> doWhileDownloading = inputStream -> {
//Use inputStream for your business logic...
};
consumerInputStreamWithoutBuffering("https://localhost.com/download", doWhileDownloading);
Run Code Online (Sandbox Code Playgroud)
请注意以下常见陷阱:
public InputStream getInputStreamFromResponse(String url) throws IOException {
final ResponseExtractor<InputStream> responseExtractor =
clientHttpResponse -> clientHttpResponse.getBody();
return restTemplate.execute(url, HttpMethod.GET, null, responseExtractor);
}
Run Code Online (Sandbox Code Playgroud)
InputStream在您可以访问之前,此处将关闭
我就是这样做解决的。希望对大家有所帮助。
@GetMapping("largeFile")
public ResponseEntity<InputStreamResource> downloadLargeFile(
@RequestParam("fileName") String fileName
) throws IOException {
RestTemplate restTemplate = new RestTemplate();
// Optional Accept header
RequestCallback requestCallback = request -> request.getHeaders()
.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
// Streams the response instead of loading it all in memory
ResponseExtractor<InputStreamResource> responseExtractor = response -> {
// Here I write the response to a file but do what you like
Path path = Paths.get("tmp/" + fileName);
Files.copy(response.getBody(), path, StandardCopyOption.REPLACE_EXISTING);
return new InputStreamResource(new FileInputStream(String.format("tmp/%s", fileName)));
};
InputStreamResource response = restTemplate.execute(
String.format("http://%s:%s/file/largeFileRestTemplate?fileName=%s", host, "9091", fileName),
HttpMethod.GET,
requestCallback,
responseExtractor
);
return ResponseEntity
.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s", fileName))
.body(response);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
30772 次 |
| 最近记录: |