增加一个 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
@Setter
public class FtpConfiguration {
private String ip;
private Integer port;
private String username;
private String password;
private String workDir;
}
配置文件中增加配置项:
ftp:
ip: 127.0.0.1
port: 21
username: username
password: pass
workDir: 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