专栏首页JAVA葵花宝典Spring RestTemplate 下载大文件

Spring RestTemplate 下载大文件

专注分享java干货技术内容

1. 概述

本教程中,我们将展示使用 RestTemplate 下载大文件的不同技术。

2. RestTemplate

RestTemplate 是 Spring 3 中引入的同步阻塞式 HTTP 客户端。根据 Spring 官方文档 介绍,在将来的版本中它可能会被弃用,因为他们已在 Spring 5 中引入了 WebClient 作为非阻塞式 Reactive HTTP 客户端。

3. 陷阱

通常,当我们下载文件时,我们会将其保存在本地文件系统中,或者作为字节流加载到内存中。但是,当遇到大文件时,内存加载可能会造成 OutOfMemoryError。因此,当我们读取 response 块时,必须将其保存到文件中。

我们先来看这两种不起作用的方法:

第一个,当我们将 Resource 作为我们的返回值类型时会发生什么?

Resource download() {
    return new ClassPathResource(locationForLargeFile);
}

ResourceHttpMesssageConverter 会将整个 response 块加载到 ByteArrayInputStream,依旧增加了我们本想避免的内存压力。

第二个,当我们配置 ResourceHttpMessageConverter#supportsReadStreaming 方法,并返回 InputStreamResource,会发生什么?好吧,它也不会起作用,**当我们调用 InputStreamResource.getInputStream() 时,将得到一个 socket closed 错误!**这是由于 execute 方法在它退出前会关闭 response 输入流所造成的。

所以我们可以怎样解决这个问题呢?实际上,也有两种办法:

  • 编写支持 File 作为返回类型的自定义的 HttpMessageConverter
  • RestTemplate.execute自定义 ResponseExtractor 一起使用,将输入流保存到文件中

基于它们的灵活性和实现难度,本教程对第二种解决方案来展开介绍。

4. 无需恢复的下载

让我们来实现一个 ResponseExtractor,用以将 body 写入到临时文件中。

File file = restTemplate.execute(FILE_URL, HttpMethod.GET, null, clientHttpResponse -> {
    File ret = File.createTempFile("download", "tmp");
    StreamUtils.copy(clientHttpResponse.getBody(), new FileOutputStream(ret));
    return ret;
});
 
Assert.assertNotNull(file);
Assertions
  .assertThat(file.length())
  .isEqualTo(contentLength);

这里我们使用 StreamUtils.copy 将 response 输入流复制到 FileOutputStream 中,同样,也可以使用 其他方式 来实现。

5. 可暂停和恢复的下载

当我们进行大文件下载时,可能会因为某些原因,我们会在暂停之后继续进行下载。

所以,第一步,我们需要检查 URL 下载链接是否支持恢复下载:

HttpHeaders headers = restTemplate.headForHeaders(FILE_URL);

Assertions
  .assertThat(headers.get("Accept-Ranges"))
  .contains("bytes");
Assertions
  .assertThat(headers.getContentLength())
  .isGreaterThan(0);

然后,我们可以实现 RequestCallback 来自定义 Range 请求头来恢复下载:

restTemplate.execute(
  FILE_URL,
  HttpMethod.GET,
  clientHttpRequest -> clientHttpRequest.getHeaders().set(
    "Range",
    String.format("bytes=%d-%d", file.length(), contentLength)),
    clientHttpResponse -> {
        StreamUtils.copy(clientHttpResponse.getBody(), new FileOutputStream(file, true));
    return file;
});

Assertions
  .assertThat(file.length())
  .isLessThanOrEqualTo(contentLength);

如果我们不确定具体的 Content 长度,可以使用如下的 String.format 形式来设置 Range 请求头

String.format("bytes=%d-", file.length())

6. 结论

我们已经讨论了大文件下载时可能会出现的问题,也给出了一种使用 RestTemplate 的解决方案,最后我们还展示了如何实现断点下载的方案。

一如既往,本文相关代码可在 GitHub 仓库中找到。

原文:https://www.baeldung.com/spring-resttemplate-download-large-file

作者:baeldung

译者:万想

本文分享自微信公众号 - JAVA葵花宝典(Javakhbd)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-06-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 使用docker部署spring cloud项目详细步骤

    (1)到Oracle官网下载好 jdk-8u181-linux-x64.tar.gz 备用 (2)卸载系统自带的java

    JAVA葵花宝典
  • Spring Cloud Security:Oauth2实现单点登录

    单点登录(Single Sign On)指的是当有多个系统需要登录时,用户只需登录一个系统,就可以访问其他需要登录的系统而无需登录。

    JAVA葵花宝典
  • 手把手教你如何免费且光荣地使用正版IntelliJ IDEA

    IDEA是个人最喜欢的IDE,它非常智能,懂我的心,极大地提高了个人编程效率;让人爱不释手,欲罢不能。

    JAVA葵花宝典
  • springmvc文件上传/下载

    需要导入fileupload依赖包 io的包 com.springsource.org.apache.commons.fileupload-1.2.0.ja...

    微醺
  • asp dotnet core 提供大文件下载的测试

    林德熙
  • python获取栅格点和面值的实现

    以上这篇python获取栅格点和面值的实现就是小编分享给大家的全部内容了,希望能给大家一个参考。

    砸漏
  • 我的工作日志 2020年11月10日 星期二

    I am working on an a11y issue and I have one doubt about --cx-color-visual-focus...

    Jerry Wang
  • 放下王者农药这锅,玩一把Tensorflow吧

    暑期开始了!对于Lady姐来说,如何安排儿子的暑期生活是一件大事,显然是不能沉迷于王者农药, ? 于是Lady姐随手扔了一个教程给他:按照这份教程,在家里Win...

    GPUS Lady
  • Codeigniter里的无刷新上传的实现代码

    Codeigniter还是很好用的,淡水一直很推崇。说是codeigniter里的无刷新上传吧,fashion 一点的说法就是利用AJAX技术上传。其中用到了J...

    砸漏
  • 一个让人遗忘的角落—Exception(三)

    最近有点事,把这个系列给落下了,给大家道个歉,这里还要感谢我的老婆,谢谢她一直对我的支持:) 系列回顾: 1.一个被人遗忘的角落—Exception(一) 2...

    脑洞的蜂蜜

扫码关注云+社区

领取腾讯云代金券