封装FTPSClient连接ftps服务器

主要是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() {}
    }
}
相关推荐
wanhengidc3 分钟前
造成云手机闪退的原因有哪些?
服务器·网络·安全·智能手机·媒体
郭二哈2 小时前
git的使用
大数据·网络·git·elasticsearch
不甘懦弱3 小时前
阿里云搭建flask服务器
服务器·python·flask
CoderJia程序员甲3 小时前
GitHub 热榜项目 - 日榜(2025-08-21)
ai·开源·github·ai编程
the sun343 小时前
Reactor设计模式及其在epoll中的应用
linux·运维·服务器·c++
VVVVWeiYee3 小时前
BGP高级特性
运维·服务器·网络
运维成长记4 小时前
Top 100 Linux Interview Questions and Answers
linux·运维·服务器
人工智能训练师4 小时前
openEuler系统中如何将docker安装在指定目录
linux·运维·服务器·人工智能·ubuntu
火车叼位5 小时前
Git 历史清理实践:彻底移除误提交的 node_modules
git