需求:从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. 隐写术之steghide的使用

    steghide不是一个软件,所以下载之后解压缩就可以在命令行中使用. win+R,cmd,回车->进入到steghide.exe所在的文件夹,使用隐藏或者解锁的相应命令,即可隐藏或者解锁. 这 ...

  2. 类Objects

    Object类是什么? 在JDK7添加了一个Objects工具类,它提供了一些方法来操作对象,它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍 ...

  3. Vue—组件传值及vuex的使用

    一.父子组件之间的传值 1.父组件向子组件传值: 子组件在props中创建一个属性,用以接收父组件传来的值 父组件中注册子组件 在子组件标签中添加子组件props中创建的属性 把需要传给子组件的值赋给 ...

  4. c#使用资源文件完成国际化

    路径结构如下 namespace UnitTestProject1 { [TestClass] public class UnitTest1 { [TestMethod] public void Te ...

  5. stm32串口接收发送数据处理

    /************************************************************* Function Name : Uart1_Send_Byte* Desc ...

  6. C#中字符串转日期类型

    1,yyyyMMdd DateTime date = DateTime.ParseExact(", "yyyyMMdd", System.Globalization.Cu ...

  7. js判断手机端

    if (window.location.toString().indexOf('pref=padindex') != -1) { } else { if (/AppleWebKit.*Mobile/i ...

  8. seaborn库

      首先找到Anaconda Prompt命令行,下载seaborn库 ,命令  pip install seaborn 1.风格设置 import seaborn as sns import num ...

  9. Nginx 过滤sub模块

    L70 通过 --with-http_sub_module 编译进nginx sub_filter 指令 Syntax: sub_filter string replacement; Default: ...

  10. bugku web 变量1

    flag In the variable ! <?php error_reporting(0);include "flag1.php";highlight_file(__fi ...