增加一个 SpringBoot 配置类:
package *;import lombok.Getter;import lombok.Setter;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Configuration;@Configuration@ConfigurationProperties(prefix = "ftp")@Getter@Setterpublic class FtpConfiguration {private String ip;private Integer port;private String username;private String password;private String workDir;}
配置文件中增加配置项:
ftp:ip: 127.0.0.1port: 21username: usernamepassword: passworkDir: json
实现一个 Spring Component:
package *;import lombok.extern.slf4j.Slf4j;import org.apache.commons.net.ftp.FTP;import org.apache.commons.net.ftp.FTPClient;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.stereotype.Component;import java.io.*;import java.nio.charset.StandardCharsets;/*** FTP 相关方法** @author Paradise* @date 2021-9-23*/@Slf4j@Component@EnableConfigurationProperties(FtpConfiguration.class)public class FtpClientComponent {private final FTPClient ftpClient;private final FtpConfiguration ftpConfiguration;/*** 本地文件缓存路径*/private final String path = System.getProperty("user.dir") + File.separator + "json";public FtpClientComponent(FtpConfiguration ftpConfiguration) {this.ftpConfiguration = ftpConfiguration;ftpClient = new FTPClient();}public File generateJsonFile(String json, String fileName) throws IOException {log.info("开始生成本地文件:{}", fileName);File file = new File(path + File.separator + fileName + ".json");if (file.exists()) {return file;}if (!file.getParentFile().exists()) {final boolean mkdirRes = file.getParentFile().mkdirs();if (!mkdirRes) {return null;}}final boolean newFile = file.createNewFile();if (!newFile) {return null;}log.info("文件创建成功~");try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {writer.write(json);writer.flush();}return file;}private void upload(File file) throws IOException {log.info("开始上传FTP文件");ftpClient.connect(ftpConfiguration.getIp(), ftpConfiguration.getPort());ftpClient.login(ftpConfiguration.getUsername(), ftpConfiguration.getPassword());log.info("ftpClient-isConnected : {} ", ftpClient.isConnected());log.info("ftpClient-isAvailable : {} ", ftpClient.isAvailable());final boolean changeWorkingDirectory = ftpClient.changeWorkingDirectory(ftpConfiguration.getWorkDir());log.info("changeWorkingDirectory : {} ", changeWorkingDirectory);final boolean setFileType = ftpClient.setFileType(FTP.BINARY_FILE_TYPE);log.info("setFileType : {} ", setFileType);// 主动模式,解决假死问题ftpClient.enterLocalPassiveMode();log.info(" 开始上传: {} ", System.currentTimeMillis());final boolean uploadRes = ftpClient.storeFile(file.getName(), new FileInputStream(file));log.info("uploadRes : {} ", uploadRes);ftpClient.disconnect();}public boolean push(String json, String fileName) {File file;try {file = this.generateJsonFile(json, fileName);if (file != null) {this.upload(file);return true;}} catch (IOException e) {log.error(e.getLocalizedMessage(), e);}return false;}}
遇到假死问题,以及解决方案:
https://www.pianshen.com/article/75811462350/
防止链接失效,复制核心内容:
【解决办法】 调用 FTPClient.listFiles()或者FTPClient.retrieveFile()方法前,先调用一下FTPClient.enterLocalPassiveMode()
【原因分析】 FTP有两种模式:主动模式(active mode) 和 被动模式(passive mode)
默认情况下是启动的主动模式。
FTP 是 TCP 链接,所以在读取文件时,要进行三次握手。
我这里在进行调试的时候,FTP Server 的防火墙是关闭的,但是服务器 Tomcat 是在我本地 PC 跑的,防火墙是开启的,即 FTP Client 开防火墙了。
当 FTP 使用主动模式时,在三次握手后,FTP Client 会开启一个>1024 的端口,FTP Server 会开启默认端口20,并主动向 FTP Client 发起数据连接请求。
而此时受我电脑防火墙的限制,FTP Server 获得 FTP Client 的数据端口后,会主动发起数据连接请求,此时会被防火墙被屏蔽的。
FTP Client无法正常接收FTP Server发送来的数据流,所以就会出现假死的现象。
如果强制FTP使用被动模式,三次握手完成后,FTP Client 会开启一个>1024 的端口,并要求 FTP Server 开启一个端口(>1024),被动等待数据通道链接的开启。 当 FTP Client 开启数据传输通道时,FTP Server 就开始被动传输数据,不存在被墙住的问题,文件就可以正常下载下来了。
【csdn】ftp的主动模式active mode和被动模式 passive mode的配置和区别 https://blog.csdn.net/zhangyuan12805/article/details/71425385
