需求:从ftp取文件并http调用某接口上传此文件

偷懒的话可以从ftp上取文件存到本地,再调用接口上传文件,如下

String ftpPath = "/ftp/path/file.bin";
RestTemplate restTemplate = new RestTemplateBuilder().build();
FtpCilent ftp = new FtpClient();
ftp.setFileType(FTP.BINARY_FILE_TYPE);
ftp.setCharset("utf-8");
ftp.setControlEncoding("utf-8");
……// ftp连接并登陆
File tmpFile = File.createTempFile(UUID.randomUUID().toString, null);
try(OutputStream outputStream = new FileOutputStream(tmpFile)){
ftp.retreiveFile(ftpPath, outputStream);// 保存到本地
}
ftp.disconnect();
MultiValueMap<String, Object> dataMap = new LinkedMultiValueMap<>();
dataMap.add("filename", new FileSystemResource(tmpFile));// 添加文件到表单
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<MultiValueMap<String, Object>>(dataMap, requestHeaders);
restTemplate.exchange(...);
tmpFile.delete();

如上即可完成需求,但如果只是这样的话我肯定不会水这一贴了,这样做有个很明显的缺点,要先将文件下载到本地,再将此文件上传,分成了两步,还多了个临时文件得删,而下面的改进版代码会将两步合并为一步提高效率并不额外占用磁盘空间。

String ftpPath = "/ftp/path/file.bin";
RestTemplate restTemplate = new RestTemplateBuilder().build();
FtpCilent ftp = new FtpClient();
ftp.setFileType(FTP.BINARY_FILE_TYPE);
ftp.setCharset("utf-8");
ftp.setControlEncoding("utf-8");
.....// ftp连接并登陆
FTPFile ftpFile = ftp.mlistFile(ftpPath);// 获取文件信息
try(InputStream in = ftp.retreiveFileStream(ftpPath);){
InputStreamResource fileResource = new InputStreamResource(in){
@Override
public long contentLength(){
return ftpFile.getSize();
} @Override
public String getFilename(){
return ftpFile.getName();
}
};
MultiValueMap<String, Object> dataMap = new LinkedMultiValueMap<>();
dataMap.add("filename", fileResource);// 添加文件到表单
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<MultiValueMap<String, Object>>(dataMap, requestHeaders);
restTemplate.exchange(...); ftp.disconnect();
}

总体思路就是将FileSystemResource换成了InputStreamResource,但这里有两个重点,我重写了它的contentLength方法和getFilename方法

先说getFilename,如果不重写这个方法,并且文件有一定大小,那么服务端会出现异常

The multi-part request contained parameter data (excluding uploaded files) that exceeded

这时你百度会看到人让你设置什么max-request-size啥的,但没用的,我试过设置几个G也没用还是上传不了几十M的文件,有必要设置文件名。

再说contentLength,如果不重写这个方法会出现异常

do not use inputstreamresource if a stream needs to be read multiple times

经排查,原因是在上传文件时resttemplate会通过这个方法得到inputstream的大小,而这个方法会直接读取inputstream的所有数据来得到大小,当它真正要读取内容的时候发现流已经被读完了,不得不说这方法实现的非常滑稽,有必要重写这个方法。

RestTemplate通过InputStreamResource上传文件的更多相关文章

  1. 使用RestTemplate调用接口上传文件

    场景 接口接受一个文件,缓存在本地,验证文件的完整性及内容,然后将文件上传至云服务器: 下面只写利用RestTemplate将文件上传至云服务器,至于文件上传以及缓存在本地可以参考:JAVA文件上传: ...

  2. 通过`RestTemplate`上传文件(InputStreamResource详解)

    通过RestTemplate上传文件 1.上传文件File 碰到一个需求,在代码中通过HTTP方式做一个验证的请求,请求的参数包含了文件类型.想想其实很简单,直接使用定义好的MultiValueMap ...

  3. Java模拟http上传文件请求(HttpURLConnection,HttpClient4.4,RestTemplate)

    先上代码: public void uploadToUrl(String fileId, String fileSetId, String formUrl) throws Throwable { St ...

  4. RestTemplate OR Spring Cloud Feign 上传文件

    SpringBoot,通过RestTemplate 或者 Spring Cloud Feign,上传文件(支持多文件上传),服务端接口是MultipartFile接收. 将文件的字节流,放入ByteA ...

  5. RestTemplate上传文件

    1.上传的文件是File类型 如果文件保存在本地,即可以通过File file = new File(path) 或者 文件路径地址获取到指定文件 public String uploadFile(F ...

  6. restTemplate.postForObject上传文件中文乱码(???.xls)

    一.问题描述 项目中, 使用restTemplate上传文件时, 文件名中文乱码, 一串问号, 源文件名为: 测试中文乱码哦哦哦.zip, 通过restTemplate.postForObject调用 ...

  7. resttemplate 文件上传 上传文件

    springmvc RestTemplate文件上传 RestTemplate上传文件总结

  8. Spring Boot 上传文件 获取项目根路径 物理地址 resttemplate上传文件

    springboot部署之后无法获取项目目录的问题: 之前看到网上有提问在开发一个springboot的项目时,在项目部署的时候遇到一个问题:就是我将项目导出为jar包,然后用java -jar 运行 ...

  9. 【Spring Boot】关于上传文件例子的剖析

    目录 Spring Boot 上传文件 功能实现 增加ControllerFileUploadController 增加ServiceStorageService 增加一个Thymeleaf页面 修改 ...

随机推荐

  1. OSI模型网络七层协议

    物理层 物理层是OSI的第一层,它虽然处于最底层,却是整个开放系统的基础.物理层为设备之间的数据通信提供传输媒体及互连设备,为数据传输提供可靠的环境. 1.1媒体和互连设备 物理层的媒体包括架空明线. ...

  2. Python--day08(文件操作)

    昨天内容回顾 1.  数据类型转换 1.  数字类型:数字类型之间可以直接  类型()进行直接转换 2.  str与int类型:int() 与str() 之间在某些条件下可以直接转换 int('10' ...

  3. Visual Studio Code扩展:

    Auto Close TagAuto Rename TagBeautifyChinese (Simplified) Language Pack for Visual Studio CodeClass ...

  4. PHP细节,empty,is_null,isset,if()

    以下内容转载自http://wuxinjie.github.io/php-04/ 从下表可知,empty与if()完全相反,is_null与isset完全相反 isset是语句,is_null是函数, ...

  5. DAY25、面向对象总复习

    面向对象总复习:面向过程编程思想: 核心是 过程 二字, 过程指的是解决问题的步骤是,即先干什么再干什么. 基于该编程思想编写程序,相当于一条流水线,一种机械式的思维方式. 面向对象编程思想: 核心是 ...

  6. 使用PHP操作ElasticSearch

    如何搭建ES环境和使用CURL操作可以参考我的另一篇文章:ElasticSearch尝试 网上很多关于ES的例子都过时了,版本很久,这篇文章的测试环境是ES6.5 通过composer 安装 comp ...

  7. DS博客作业02--线性表

    1.本周学习总结 1.1思维导图 1.2.谈谈你对线性表的认识及学习体会 这阶段学习学的是线性表,学习线性表的两种存储顺序-----链表和顺序表,体会了两者存储结构之间的区别,通过对顺序表,单链表,双 ...

  8. python之单例模式、栈、队列和有序字典

    一.单例模式 import time import threading class Singleton(object): lock = threading.RLock() # 定义一把锁 __inst ...

  9. [题解]小X的液体混合

    版权说明:来自 石门ss学校 Guohao OJ ,禁止转载 题目描述 虽然小X不喜欢化学原理,但他特别喜欢把一大堆液体倒在一起. 现在小X有n种液体,其中m对会发生反应.现在他想把这n种液体按某种顺 ...

  10. Oracle表之间关联更新

    经常会遇到一个表需要根据另一个表数据来更新数据,总结了核心的sql脚本命令如下: A表如下x        y--------------ka       dakb       dbkc       ...