首页
Preview

使用 REST API 传输大文件

简介

本周,我的团队和我遇到了一个问题,这是我在大学时第一次遇到的。自那以后,我完全忘记了它,直到今年十月的星期三:如何通过HTTP传输一个非常大的文件。

需求和设计

我们的客户用云端的CRM替换了原来的CRM,并要求我们将其与整个软件映射集成。

其中一个集成流程是从本地存储库中发布文档到CRM,并将其与存储的客户帐户相关联;文件大小没有上限,我们假定1 GB是中等值。

所有CRM集成都是基于REST的,没有共享文件夹,没有分段数据库,只允许使用REST API OAUTH1进行安全保护。

下面是我为你勾画的简化的架构模型图。

图1 — 解决方案架构

我们的应用程序与应用程序映射的其他部分一样,运行在本地环境中,而CRM则托管在云租户上。

暴露的API接受一个_Multipart_主体,其中包含两个部分:一个包含元数据的JSON文档,例如文件名、客户帐户ID等,另一个是文件的二进制内容。

标准解决方案

该应用程序由两部分组成:第一部分是文件轮询器,每当在分段文件夹中看到新文件时,就创建一个线程,将其与客户端帐户相关联并发送到CRM。

如果你有兴趣创建一个文件轮询器,我向你提供一个链接:Apache Camel轮询消费者,它是一个很好的解决方案,可以轻松地实现。

在这里,我想谈谈我们如何将文件发送到CRM。

让我们开始编码;这里是我们_pom.xml_的一个摘录:

<dependency>
   <groupId>org.apache.httpcomponents</groupId>      <artifactId>httpclient</artifactId>
</dependency><dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
</dependency>

这里是我们标准解决方案的代码

RestTemplate remoteService = new RestTemplate();//HTTP has two parts: Header and Body//Here is the header:HttpHeader header = new HttpHeader();//Here is the bodyMultiValueMap<String,Object>  bodyMap =  new MultiValueMap<String,Object>();
bodyMap.add(“customer_file”,new FileSystemResource(fileName));
bodyMap.add(“customer_name”, customerJSON);HttpEntity<MultiValueMap> request = new HttpEntity(bodyMap, header);
ResponseEntity<String> restResponse = remoteService .exchange(remoteServiceURL, HttpMethod.POST, request, String);

customerJson_变量是一个_javax.json.JsonObject;以这种方式,多部分请求会自动选择正确的内容类型,而使用_org.springframework.core.io.FileSystemResource_实例时也期望出现相同的行为。

我们进行了以下测试:

  • 发送一个小文件,以查找一些格式不正确的请求
  • 发送一个巨大的文件,以证明我们的应用程序的稳健性

对于测试1,我们没有遇到任何重要问题,只是一些缺少标题值、格式不正确的URL输入等等。对于测试2,我们等了几分钟,然后出现了所有Java开发人员的噩梦。

java.lang.OutOfMemoryError: Java heap space

这个问题不仅仅是因为我们在开发环境中运行了代码,而且因为应用程序试图将整个文件内容加载到RAM中,使其比J. Wellington Wimpy还要耗费更多的内存。

从简单的应用程序内存占用来看,这是显而易见的。

总之,从架构的角度来看,这不是一个好的解决方案,因为:

  • 我们无法假设传入文件的最大大小
  • 我们无法按顺序处理文件

我们需要改进它。

分块解决方案

我们需要训练我们的代码,不要将整个文件内容加载到内存中,而是使用HTTP1.1支持的功能,即直到我在大学里的那个年代才出现的分块传输编码

该功能告诉服务器,传入请求由多个HTTP消息组成,并且需要接收所有消息才能开始处理。

从客户端的角度来看,优点在于你只在你正在传输的那一部分中加载内存。

如果你想了解更多关于HTTP如何实现分块传输编码的内容,请参考这些WIKIW3C

我们通过适当配置RestTemplate来改进我们的类:

RestTemplate remoteService = new RestTemplate();SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();requestFactory.setBufferRequestBody(false); remoteService.setRequestFactory(requestFactory);

我们重复了测试2,这次一切都很顺利。

我们观察到完整传输1.5 GB文件时的内存占用少于300 MB!成功了!

结论和问候

在本文中,我描述了我们发现的传输大型文件的解决方案;你可以使用不同的库找到多个解决方案。

我想补充一点,这个功能只有在HTTP1.1中才有,而HTTP 2不再支持分块传输编码;我认为你需要寻找某种流式API。

在这里,我们选择使用众所周知的RestTemplate类,而不是较新的WebClient:我无法告诉你是否可以适应它。

译自:https://medium.com/swlh/transfer-large-files-using-a-rest-api-a0aa96983ebb

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
阿波
The minute I see you, I want your clothes gone!

评论(0)

添加评论