主要是connect方法中一些set方法操作是必要的
支持try with resource
java
@Slf4j
public class FtpsClient implements AutoCloseable {
private final String host;
private final int port;
private final String username;
private final String password;
private final int connectTimeout;
private FTPSClient ftpsClient;
public FtpsClient(String host, int port, String username, String password) {
this(host, port, username, password, 10000);
}
public FtpsClient(String host, int port, String username, String password, int connectTimeout) {
this.host = host;
this.port = port;
this.username = username;
this.password = password;
this.connectTimeout = connectTimeout;
}
/**
* 连接到FTPS服务器
*/
public boolean connect() {
if (isConnected()) {
return true;
}
try {
log.debug("[FtpsClient] Initializing FTPS client");
ftpsClient = new FTPSClient();
ftpsClient.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager());
ftpsClient.setEndpointCheckingEnabled(false);
ftpsClient.setConnectTimeout(connectTimeout);
ftpsClient.setDataTimeout(Duration.ofSeconds(30));
FTPClientConfig config = new FTPClientConfig();
ftpsClient.configure(config);
ftpsClient.addProtocolCommandListener(new PrintCommandListener(
// 打印ftp服务器日志,重定向到Slf4j
new PrintWriter(new LogWriter(log))
));
ftpsClient.setControlEncoding("UTF-8");
log.debug("[FtpsClient] Connecting to {}:{}", host, port);
ftpsClient.connect(host, port);
int reply = ftpsClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
throw new IOException("FTPS server refused connection: " + reply);
}
if (!ftpsClient.login(username, password)) {
throw new IOException("Login failed for user: " + username);
}
ftpsClient.enterLocalPassiveMode();
ftpsClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
// 数据通道加密
ftpsClient.execPROT("P");
ftpsClient.setFileType(FTP.BINARY_FILE_TYPE);
// 1MB buffer
ftpsClient.setBufferSize(1024 * 1024);
log.info("[FtpsClient] Connected to FTPS server: {}:{}", host, port);
return true;
} catch (Exception e) {
log.error("[FtpsClient] Connection failed to {}:{} - {}", host, port, e.getMessage());
disconnectSilently();
throw new RuntimeException("FTPS connection failed: " + e.getMessage(), e);
}
}
/**
* 返回底层FTPS客户端实例
*/
public FTPSClient operation() {
connectedCheck();
return ftpsClient;
}
/**
* 上传文件到FTPS服务器
*
* @param localFilePath 本地文件路径
* @param remoteDirectory 远程目录路径
* @return 是否上传成功
*/
public boolean uploadFile(String localFilePath, String remoteDirectory) {
connectedCheck();
File localFile = new File(localFilePath);
try (InputStream is = Files.newInputStream(Paths.get(localFilePath))) {
createRemoteDirectory(remoteDirectory);
String remotePath = remoteDirectory + "/" + localFile.getName();
log.debug("[FtpsClient] Uploading file: {}", localFilePath);
if (!ftpsClient.storeFile(remotePath, is)) {
throw new IOException("Upload failed. Server reply: " + ftpsClient.getReplyString());
}
verifyUploadSuccess(localFile, remotePath);
log.info("[FtpsClient] File uploaded successfully: {}", remotePath);
return true;
} catch (Exception e) {
log.error("[FtpsClient] Upload failed: {}", e.getMessage());
throw new RuntimeException("FTPS upload failed: " + e.getMessage(), e);
}
}
/**
* 从FTPS服务器下载文件
*
* @param remoteFilePath 远程文件路径
* @param localFilePath 本地存储文件路径
* @return 是否下载成功
*/
public boolean retrieveFile(String remoteFilePath, String localFilePath) {
connectedCheck();
try (OutputStream os = Files.newOutputStream(Paths.get(localFilePath))) {
log.debug("[FtpsClient] Downloading file: {}", remoteFilePath);
if (!ftpsClient.retrieveFile(remoteFilePath, os)) {
throw new IOException("Download failed. Server reply: " + ftpsClient.getReplyString());
}
File localFile = new File(localFilePath);
if (!localFile.exists() || localFile.length() == 0) {
throw new IOException("Local file not created or empty");
}
log.info("[FtpsClient] File downloaded successfully: {}", localFilePath);
return true;
} catch (Exception e) {
log.error("[FtpsClient] Download failed: {}", e.getMessage());
throw new RuntimeException("FTPS download failed: " + e.getMessage(), e);
}
}
/**
* 递归创建远程目录
*/
public boolean createRemoteDirectory(String path) throws IOException {
connectedCheck();
if (path == null || path.isEmpty()) {
return true;
}
String[] dirs = path.split("/");
StringBuilder currentPath = new StringBuilder();
for (String dir : dirs) {
if (dir.isEmpty()) {
continue;
}
currentPath.append("/").append(dir);
String dirPath = currentPath.toString();
if (!ftpsClient.changeWorkingDirectory(dirPath)) {
if (ftpsClient.makeDirectory(dirPath)) {
log.debug("[FtpsClient] Created directory: {}", dirPath);
} else {
throw new IOException("Failed to create directory: " + dirPath);
}
ftpsClient.changeWorkingDirectory(dirPath);
}
}
return true;
}
/**
* 验证上传文件完整性
*
* @param localFile 本地文件
* @param remotePath 远程路径
* @throws IOException 抛出异常
*/
private void verifyUploadSuccess(File localFile, String remotePath) throws IOException {
long localSize = localFile.length();
long remoteSize = ftpsClient.listFiles(remotePath)[0].getSize();
if (localSize != remoteSize) {
throw new IOException(String.format(
"Size mismatch! Local: %d bytes, Remote: %d bytes",
localSize, remoteSize
));
}
log.debug("[FtpsClient] File verification passed: {} bytes", remoteSize);
}
/**
* 连接状态检查
*/
private void connectedCheck() {
if (!isConnected()) {
throw new IllegalStateException("FTPS connection not established");
}
}
/**
* 判断连接状态
* @return boolean
*/
public boolean isConnected() {
return ftpsClient != null && ftpsClient.isConnected();
}
/**
* 静默断开连接
*/
private void disconnectSilently() {
try {
close();
} catch (Exception e) {
log.debug("[FtpsClient] Error during disconnect: {}", e.getMessage());
}
}
@Override
public void close() {
if (ftpsClient != null) {
try {
if (ftpsClient.isConnected()) {
ftpsClient.logout();
}
} catch (IOException e) {
log.debug("[FtpsClient] Logout failed: {}", e.getMessage());
} finally {
try {
ftpsClient.disconnect();
} catch (IOException e) {
log.debug("[FtpsClient] Disconnect failed: {}", e.getMessage());
}
}
}
}
/**
* 日志重定向辅助类
* @author changxin.man
* @date 2025/08/21
*/
private static class LogWriter extends Writer {
private final org.slf4j.Logger logger;
LogWriter(org.slf4j.Logger logger) {
this.logger = logger;
}
@Override
public void write(char[] cbuf, int off, int len) {
if (logger.isDebugEnabled()) {
String message = new String(cbuf, off, len).trim();
if (!message.isEmpty()) {
logger.debug("[FTPS] {}", message);
}
}
}
@Override
public void flush() {}
@Override
public void close() {}
}
}