博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
RestTemplate 实现大文件下载的坑与各种姿势
阅读量:1906 次
发布时间:2019-04-26

本文共 2618 字,大约阅读时间需要 8 分钟。

点击蓝色“程序猿DD”关注我哟

加个“星标”,不忘签到哦

关注我,回复口令获取可获取独家整理的学习资料:

001领取《Spring Boot基础教程》

002领取《Spring Cloud基础教程》

- 003:领取《Java开发规范1.5》最新版)

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 仓库中找到。

推荐关注

推荐一个专注于国外大佬文章的翻译与发布的公众号。通过这个公众号,可以帮助我们紧跟技术的国际化发展趋势。

640?wx_fmt=png

推荐阅读

自律到极致 - 人生才精致:第9期

正在筹备中!

640?wx_fmt=jpeg

关注我,加个星标,不忘签到哦~

2019

与大家聊聊技术人的斜杠生活

640?wx_fmt=png

点一点“阅读原文”小惊喜在等你

转载地址:http://tpvcf.baihongyu.com/

你可能感兴趣的文章
javaWeb校园二手平台项目
查看>>
java的陶瓷工厂进销存管理系统的设计与实现
查看>>
java物流网站的设计与实现
查看>>
基于java的企业车辆管理系统的设计与实现
查看>>
基于java的企业员工管理系统的设计与实现
查看>>
基于java的赛北村旅游网站的设计与实现
查看>>
基于java的搜索引擎的设计与实现
查看>>
基于java的陶瓷工厂进销存管理系统的设计与实现
查看>>
基于java的网络考试系统的设计与实现
查看>>
基于java的网络爬虫技术的网络新闻分析
查看>>
病历管理系统设计与实现
查看>>
高校固定资产管理系统
查看>>
关于java博网即时通讯软件的设计与实现
查看>>
基于JAVA_JSP电子书下载系统
查看>>
基于java出租车计价器设计与实现
查看>>
基于java的B2C的网上拍卖系统
查看>>
基于java的百货中心供应链管理系统
查看>>
基于java的保险业务管理系统的设计与实现
查看>>
基于java的采购管理系统
查看>>
基于java的仓库管理系统的设计与实现
查看>>