Rest template read response as stream and pass to spring controller(Get InputStream with rest Template)

huangapple go评论71阅读模式
英文:

Rest template read response as stream and pass to spring controller(Get InputStream with rest Template)

问题

我有一个下载大型zip文件的URL。它以流的形式返回响应。尽管文件大小很大,但首先会返回200(HTTP状态码为OK),然后继续下载。

我必须实现一个新的Spring控制器,通过RestTemplate调用上述URL。我必须读取RestTemplate返回的响应,并传递给控制器。最初我是这样实现的:

@GetMapping("/export/downloadFile")
public ResponseEntity<byte[]> downloadData(Model model,
                                           @ModelAttribute(EXCEPTION_COLLECTOR) ExceptionCollector exceptionCollector,
                                           @RequestParam("userName") String userName,
                                           @RequestParam("startDate") Date startDate,
                                           @RequestParam("endDate") Date endDate,
                                           @RequestParam("reason") String reason) {

   URI uri = // 构建URL;

    return restTemplate.exchange(uri, HttpMethod.GET, new HttpEntity<>(httpHeaders), byte[].class);

}

因为我使用了ResponseEntity<byte[]>,RestTemplate会等待整个文件加载到内存中。因此,我经常遇到套接字超时问题。

我们是否有方法以流的形式读取响应并返回给控制器?

我找到了一些关于restTemplate.execute的内容。

restTemplate.execute(uri, HttpMethod.GET, requestCallBack, clientHttpResponse -> {
                File ret = File.createTempFile("download", ".zip", new File("/Users/bokkavijay/Desktop"));
                StreamUtils.copy(clientHttpResponse.getBody(), new FileOutputStream(ret));
                return ret;
            });

上面的代码片段可以将文件复制到本地而不会超时,但这不是我想要的。

我们如何将clientHttpResponse中的流导入到控制器中?

英文:

I have a url which download the large size zip file.It returns the response as stream.though file size is large first it returns 200(HTTPSTATUK.OK) and continues download.

I have to implement a new spring controller which call the above url through rest template.I have to read the response returned by rest template and pass to controller.initially I have implemented in below way

     @GetMapping(&quot;/export/downloadFile&quot;)
        public ResponseEntity&lt;byte[]&gt; downloadData(Model model,
                                                       @ModelAttribute(EXCEPTION_COLLECTOR) ExceptionCollector exceptionCollector,
                                                       @RequestParam(&quot;userName&quot;) String userName,
                                                       @RequestParam(&quot;startDate&quot;) Date startDate,
                                                       @RequestParam(&quot;endDate&quot;) Date endDate,
                                                       @RequestParam(&quot;reason&quot;) String reason) {

   URI uri = /building url here/;
    
    return restTemplate.exchange(uri, HttpMethod.GET, new HttpEntity&lt;&gt;(httpHeaders), byte[].class);
    
    }

since I am using ResponseEntity<byte[]> , rest template waits till entire file loaded into memory.so very frequently I am getting socket timeout issue.

Do we have way to read the response as stream and return to controller.

I found few things about restTemplate.execute .

restTemplate.execute(uri,HttpMethod.GET,requestCallBack,clientHttpResponse -&gt; {
                File ret = File.createTempFile(&quot;download&quot;, &quot;.zip&quot;,new File(&quot;/Users/bokkavijay/Desktop&quot;));
                StreamUtils.copy(clientHttpResponse.getBody(), new FileOutputStream(ret));
                return ret;
            });

above snippet can copy the file to our local with out time out but this is not what I need.

how can we pipe the stream in clientHttpResponse to controller ?

答案1

得分: 0

我找到了可工作的实现方式。

控制器:

@GetMapping("/readResponseAsStream")
public ResponseEntity<StreamingResponseBody> downloadAsStream(Model model, HttpServletResponse response) {

    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.add("Transfer-Encoding", "chunked");
    httpHeaders.add("Content-Type", "x-zip-compressed");
    httpHeaders.add("Content-Disposition", "attachment;filename=sample.zip");

    ServletOutputStream servletOutputStream = response.getOutputStream();

    StreamingResponseBody downloadFile = out -> {

        RequestCallback requestCallBack = request -> {
            // 在这里添加请求头
            request.getHeaders().add(/*添加请求头内容*/);
        };

        ResponseExtractor<ServletOutputStream> responseExtractor = clientHttpResponse -> {

            // 如果你想将响应流写入 HttpServletResponse,可以在这里添加代码片段
            byte[] buff = new byte[800000];
            int bytesRead = 0;
            while ((bytesRead = clientHttpResponse.getBody().read(buff)) != -1) {
                servletOutputStream.write(buff, 0, bytesRead);
            }
            return servletOutputStream;

            // 如果你想要将文件复制到本地
            File ret = File.createTempFile("download", ".zip", new File("在这里添加本地系统目录地址"));
            StreamUtils.copy(clientHttpResponse.getBody(), new FileOutputStream(ret));

            // 你可以将 clientHttpResponse.getBody() 复制到 ByteArrayInputStream 并返回
            // 不要直接返回 clientHttpResponse.getBody(),因为 RestTemplate 在 .execute 完成后会关闭 inputStream(clientHttpResponse.getBody())

            // 如果你想要在 DAO 层中编写 restTemplate.execute,将 servletOutputStream 作为参数传递给方法

        };

        restTemplate.execute(URL_ADDRESS, HttpMethod.GET, requestCallBack, responseExtractor);

    };

    return new ResponseEntity(downloadFile, httpHeaders, HttpStatus.OK);
}

如果你将响应直接写入 HttpServletResponse,当我们在浏览器中访问时,控制器会下载该文件。

英文:

I found the working implementation

Controller

    @GetMapping(&quot;/readResponseAsStream&quot;)
public ResponseEntity&lt;StreamingResponseBody&gt; downloadAsStream(Model model,HttpServletResponse response) {
HttpHeaders httpHeaders=new HttpHeaders();
httpHeaders.add(&quot;Transfer-Encoding&quot;,&quot;chunked&quot;);
httpHeaders.add(&quot;Content-Type&quot;,&quot;x-zip-compressed&quot;);
httpHeaders.add(&quot;Content-Disposition&quot;, &quot;attachment;filename=sample.zip&quot;);
ServletOutputStream servletOutputStream=response.getOutputStream();
StreamingResponseBody downloadFile = out -&gt; {
RequestCallback requestCallBack=request-&gt;{
request.getHeaders().add(//Add headers here);
};
ResponseExtractor&lt;ServletOutputStream&gt; responseExtractor = clientHttpResponse -&gt; {
//code snippet if you want to write response stream to HttpServletResponse
byte[] buff = new byte[800000];
int bytesRead = 0;
while ((bytesRead = clientHttpResponse.getBody().read(buff)) != -1) {
servletOutputStream.write(buff, 0, bytesRead);
}
return servletOutputStream;
//Incase if you want to copy file to you local
File ret = File.createTempFile(&quot;download&quot;, &quot;.zip&quot;,new File(&quot;Add Local system directory address here&quot;));
StreamUtils.copy(clientHttpResponse.getBody(), new FileOutputStream(ret));
//You can copy the the clientHttpResponse.getBody() to ByteArrayInputStream and return
// Don&#39;t return clientHttpResponse.getBody() directly because rest template will close the inputStream(clientHttpResponse.getBody()) after .execute completed
//if you want to write restTemplate.execute in dao layer , pass servletOutputStream as a argument to method
};
restTemplate.execute(URL_ADDRESS,HttpMethod.GET,requestCallBack,responseExtractor);
};
return new ResponseEntity(downloadFile,httpHeaders,HttpStatus.OK);
}

If you write the response directly to HttpServletResponse , controller download the file when we access in browser

huangapple
  • 本文由 发表于 2020年10月8日 17:13:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/64259342.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定