Spring RestTemplate将流响应转换为另一个请求

时间:2017-01-25 15:54:58

标签: java spring spring-mvc

我正在尝试使用spring RestTemplate

将文件下载的结果直接传输到另一篇文章中

我目前的做法如下:

   ResponseEntity<InputStreamResource> downloadResponse = restTemplate.getForEntity(fileToDownloadUri, InputStreamResource.class);

   InputStreamResource imageInputStreamResource = downloadResponse.getBody();

   ResponseEntity<String> response = restTemplate.exchange(storageUri, POST, new HttpEntity<>(imageInputStreamResource), String.class);

但是,运行上面的代码时出现以下异常:

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://host:port/path/some.jpg": stream is closed; nested exception is java.io.IOException: stream is closed

    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:6
...
Caused by: java.io.IOException: stream is closed
    at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.ensureOpen(HttpURLConnection.java:3348)
    at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:3373)

似乎响应总是作为处理的最后一步关闭。通过响应,HttpURLConnection将关闭,并且流不再可处理。

我希望能够实现此方案,而无需将文件完全保存在内存中或将其写入文件(如here所述)。

任何提示都受到高度赞赏。

2 个答案:

答案 0 :(得分:17)

如果您希望直接转发响应而不将其保留在内存中,则必须直接写入响应:

@RequestMapping(value = "/yourEndPoint")
public void processRequest(HttpServletResponse response) {
    RestTemplate restTemplate = new RestTemplate();

    response.setStatus(HttpStatus.OK.value());

    restTemplate.execute(
        fileToDownloadUri,
        HttpMethod.GET,
        (ClientHttpRequest requestCallback) -> {},
        responseExtractor -> {
            IOUtils.copy(responseExtractor.getBody(), response.getOutputStream());
            return null;
        });
}

答案 1 :(得分:1)

由于您告诉RestTemplate期望InputStreamResource,它将尝试使用适当的转换器将您的消息转换为InputStreamResource。 (我猜测没有可以根据你的需要处理这个问题)

您应该能够让它从您可以获取输入流的资源中获取并阅读该资源。

 import org.springframework.core.io.Resource;

 ResponseEntity<Resource> exchange = RestTemplate.exchange(url, HttpMethod.GET, new HttpEntity(httpHeaders), Resource.class);
 InputStream inputStream = exchange.getBody().getInputStream();

使用此功能,您可以将响应写入其他地方。 Files.write(inputStream, new File("./test.json"));为我写了文件,所以我假设输入流也可以在其他地方使用。 (我使用Spring 4.3.5)

修改

正如OP所述,这仍然会将文件加载到内存中。在场景后面,InputStream是一个ByteArrayInputStream。

默认情况下,RestTemplate和MessageConverters根本不用于流媒体内容。 您可以编写自己的org.springframework.web.client.ResponseExtractor实现,也可以编写MessageConverter。在ResponseExtractor中,您可以访问org.springframework.http.client.ClientHttpResponse

imho用于您的用例,您可能更好地使用Apache Httpcomponents HttpClient找到HttpEntity#writeTo(OutputStream)