package cd.casic.ci.process.util;

import cd.casic.ci.process.process.dataObject.aflManager.AflInfo;
import cd.casic.ci.process.process.dataObject.aflManager.AflPlotInfo;
import cd.casic.framework.commons.exception.ServiceException;
import com.amazonaws.util.IOUtils;
import com.jcraft.jsch.*;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;

import static com.fhs.common.utils.StringUtil.trim;
@Slf4j
public class SftpUploadUtil {

    private static final int DEFAULT_SFTP_PORT = 22;

    /**
     * 通过 SFTP 上传文件到远程服务器
     *
     * @param remoteHost       远程服务器IP或主机名
     * @param remotePort       远程服务器端口 (通常是 22)，为 null 或 <= 0 时使用默认端口 22
     * @param username         远程服务器用户名
     * @param password         远程服务器密码 (如果使用密码认证)
     * @param sshKeyPath       SSH Key 文件路径 (如果使用密钥认证，password 参数可以为 null)
     * @param localFilePath    本地要上传的文件路径
     * @param remoteDir        远程服务器存放文件的目录 (例如: /home/user/uploads/)
     * @param remoteFileName   上传到远程服务器的文件名 (为 null 或空字符串时使用本地文件名)
     * @throws SftpUploadException 如果上传过程中发生任何错误
     */
    public static void uploadFileViaSftp(
            String remoteHost,
            Integer remotePort,
            String username,
            String password,
            String sshKeyPath,
            String localFilePath,
            String remoteDir,
            String remoteFileName) throws SftpUploadException {

        Session session = null;
        Channel channel = null;
        ChannelSftp channelSftp = null;
        FileInputStream fis = null;

        try {
            JSch jsch = new JSch();

            // 1. 添加身份认证信息 (密码或密钥)
            if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
                // 使用 SSH Key 认证
                File sshKeyFile = new File(sshKeyPath);
                if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
                    throw new SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
                }
                jsch.addIdentity(sshKeyPath);
                System.out.println("使用 SSH Key 认证: " + sshKeyPath);
            } else if (password == null || password.trim().isEmpty()) {
                // 如果没有提供密码或密钥路径，则认证信息不全
                throw new SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
            }
            // 如果提供了密码，将在 getSession 后设置，因为 getSession 需要用户名、主机和端口先建立连接意图


            // 2. 获取 Session
            int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
            session = jsch.getSession(username, remoteHost, port);
            System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);


            // 如果使用密码认证且提供了密码
            if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
                session.setPassword(password);
                System.out.println("使用密码认证.");
            }

            // 设置连接不进行主机密钥检查 (生产环境不推荐，应该配置 known_hosts)
            // 在实际应用中，应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no"); // !!! 生产环境请谨慎使用或配置正确的主机密钥检查 !!!
            session.setConfig(config);

            // 3. 连接 Session
            session.connect();
            System.out.println("SFTP Session 连接成功.");

            // 4. 打开 SFTP Channel
            channel = session.openChannel("sftp");
            channel.connect();
            System.out.println("SFTP Channel 打开成功.");

            channelSftp = (ChannelSftp) channel;

            // 5. 检查并切换到远程目录
            try {
                channelSftp.cd(remoteDir);
                System.out.println("已切换到远程目录: " + remoteDir);
            } catch (SftpException e) {
                // 如果远程目录不存在，尝试创建
                System.err.println("远程目录不存在: " + remoteDir + "，尝试创建...");
                try {
                    // 尝试递归创建目录 (如果需要)
                    createRemoteDirRecursive(channelSftp, remoteDir);
                    channelSftp.cd(remoteDir); // 创建后再次切换
                    System.out.println("远程目录创建成功并已切换: " + remoteDir);
                } catch (SftpException e2) {
                    // 创建目录失败
                    throw new SftpUploadException("远程目录创建失败: " + remoteDir, e2);
                }
            }

            // 6. 获取本地文件流
            File localFile = new File(localFilePath);
            if (!localFile.exists() || !localFile.isFile()) {
                throw new SftpUploadException("本地文件不存在或不是一个有效文件: " + localFilePath);
            }
            fis = new FileInputStream(localFile);
            System.out.println("本地文件流获取成功: " + localFilePath);

            // 7. 确定远程文件名
            String finalRemoteFileName = (remoteFileName != null && !remoteFileName.trim().isEmpty()) ?
                    remoteFileName : localFile.getName();
            System.out.println("最终上传到远程的文件名为: " + finalRemoteFileName);

            // 8. 上传文件
            channelSftp.put(fis, finalRemoteFileName);
            System.out.println("文件上传成功!");

        } catch (JSchException e) {
            throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
        } catch (SftpException e) {
            throw new SftpUploadException("SFTP 操作失败 (如切换目录或上传文件): " + e.getMessage(), e);
        } catch (FileNotFoundException e) {
            throw new SftpUploadException("本地文件未找到: " + e.getMessage(), e);
        } catch (IOException e) {
            throw new SftpUploadException("文件流读写异常: " + e.getMessage(), e);
        } catch (SftpUploadException e) {
            // 重新抛出自定义异常
            throw e;
        } catch (Exception e) {
            // 捕获其他未知异常
            throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
        } finally {
            // 9. 关闭资源 (确保在任何情况下都关闭)
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    System.err.println("关闭本地文件流失败: " + e.getMessage());
                    e.printStackTrace(); // 打印堆栈以便调试
                }
            }
            if (channelSftp != null) {
                channelSftp.disconnect();
                System.out.println("SFTP Channel 已断开.");
            }
            if (channel != null) {
                channel.disconnect();
                System.out.println("SFTP Channel 资源已释放.");
            }
            if (session != null) {
                session.disconnect();
                System.out.println("SFTP Session 已断开.");
            }
        }
    }



    /**
     * 通过 SFTP 上传文件到远程服务器
     *
     * @param remoteHost     远程服务器IP或主机名
     * @param remotePort     远程服务器端口 (通常是 22)，为 null 或 <= 0 时使用默认端口 22
     * @param username       远程服务器用户名
     * @param password       远程服务器密码 (如果使用密码认证)
     * @param sshKeyPath     SSH Key 文件路径 (如果使用密钥认证，password 参数可以为 null)
     * @param file           本地要上传的文件路径
     * @param remoteDir      远程服务器存放文件的目录 (例如: /home/user/uploads/)
     * @param remoteFileName 上传到远程服务器的文件名 (为 null 或空字符串时使用本地文件名)
     * @return
     * @throws SftpUploadException 如果上传过程中发生任何错误
     */
    public static String uploadFileSftp(
            String remoteHost,
            Integer remotePort,
            String username,
            String password,
            String sshKeyPath,
            MultipartFile file,
            String remoteDir,
            String remoteFileName) throws SftpUploadException {

        Session session = null;
        Channel channel = null;
        ChannelSftp channelSftp = null;
        InputStream fis = null;

        try {
            JSch jsch = new JSch();

            // 1. 添加身份认证信息 (密码或密钥)
            if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
                // 使用 SSH Key 认证
                File sshKeyFile = new File(sshKeyPath);
                if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
                    throw new SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
                }
                jsch.addIdentity(sshKeyPath);
                System.out.println("使用 SSH Key 认证: " + sshKeyPath);
            } else if (password == null || password.trim().isEmpty()) {
                // 如果没有提供密码或密钥路径，则认证信息不全
                throw new SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
            }
            // 如果提供了密码，将在 getSession 后设置，因为 getSession 需要用户名、主机和端口先建立连接意图


            // 2. 获取 Session
            int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
            session = jsch.getSession(username, remoteHost, port);
            System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);


            // 如果使用密码认证且提供了密码
            if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
                session.setPassword(password);
                System.out.println("使用密码认证.");
            }

            // 设置连接不进行主机密钥检查 (生产环境不推荐，应该配置 known_hosts)
            // 在实际应用中，应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no"); // !!! 生产环境请谨慎使用或配置正确的主机密钥检查 !!!
            session.setConfig(config);

            // 3. 连接 Session
            session.connect();
            System.out.println("SFTP Session 连接成功.");

            // 4. 打开 SFTP Channel
            channel = session.openChannel("sftp");
            channel.connect();
            System.out.println("SFTP Channel 打开成功.");

            channelSftp = (ChannelSftp) channel;

            // 5. 检查并切换到远程目录
            try {
                channelSftp.cd(remoteDir);
                System.out.println("已切换到远程目录: " + remoteDir);
            } catch (SftpException e) {
                // 如果远程目录不存在，尝试创建
                System.err.println("远程目录不存在: " + remoteDir + "，尝试创建...");
                try {
                    // 尝试递归创建目录 (如果需要)
                    createRemoteDirRecursive(channelSftp, remoteDir);
                    channelSftp.cd(remoteDir); // 创建后再次切换
                    System.out.println("远程目录创建成功并已切换: " + remoteDir);
                } catch (SftpException e2) {
                    // 创建目录失败
                    throw new SftpUploadException("远程目录创建失败: " + remoteDir, e2);
                }
            }

            // 6. 获取本地文件流
            fis = file.getInputStream();
            System.out.println("本地文件流获取成功: " + file);

            // 7. 确定远程文件名
            String finalRemoteFileName = (remoteFileName != null && !remoteFileName.trim().isEmpty()) ?
                    remoteFileName : file.getOriginalFilename();
            System.out.println("最终上传到远程的文件名为: " + finalRemoteFileName);

            // 8. 上传文件
            channelSftp.put(fis, finalRemoteFileName);
            System.out.println("文件上传成功!");
            return remoteDir + "/" + finalRemoteFileName;
        } catch (JSchException e) {
            throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
        } catch (SftpException e) {
            throw new SftpUploadException("SFTP 操作失败 (如切换目录或上传文件): " + e.getMessage(), e);
        } catch (FileNotFoundException e) {
            throw new SftpUploadException("本地文件未找到: " + e.getMessage(), e);
        } catch (IOException e) {
            throw new SftpUploadException("文件流读写异常: " + e.getMessage(), e);
        } catch (SftpUploadException e) {
            // 重新抛出自定义异常
            throw e;
        } catch (Exception e) {
            // 捕获其他未知异常
            throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
        } finally {
            // 9. 关闭资源 (确保在任何情况下都关闭)
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    System.err.println("关闭本地文件流失败: " + e.getMessage());
                    e.printStackTrace(); // 打印堆栈以便调试
                }
            }
            if (channelSftp != null) {
                channelSftp.disconnect();
                System.out.println("SFTP Channel 已断开.");
            }
            if (channel != null) {
                channel.disconnect();
                System.out.println("SFTP Channel 资源已释放.");
            }
            if (session != null) {
                session.disconnect();
                System.out.println("SFTP Session 已断开.");
            }
        }
    }



    /**
     * 通过 SFTP 上传文件到远程服务器
     *
     * @param remoteHost       远程服务器IP或主机名
     * @param remotePort       远程服务器端口 (通常是 22)，为 null 或 <= 0 时使用默认端口 22
     * @param username         远程服务器用户名
     * @param password         远程服务器密码 (如果使用密码认证)
     * @param sshKeyPath       SSH Key 文件路径 (如果使用密钥认证，password 参数可以为 null)
     * @param remoteFilePath    要下载的文件地址（包含文件名）
     * @throws SftpUploadException 如果上传过程中发生任何错误
     */
    public static void downloadFileSftp(
            String remoteHost,
            Integer remotePort,
            String username,
            String password,
            String sshKeyPath,
            String remoteFilePath,
            HttpServletResponse response) throws SftpUploadException {

        Session session = null;
        Channel channel = null;
        ChannelSftp channelSftp = null;
        InputStream inputStream = null;

        try {
            JSch jsch = new JSch();

            // 1. 添加身份认证信息 (密码或密钥)
            if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
                // 使用 SSH Key 认证
                File sshKeyFile = new File(sshKeyPath);
                if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
                    throw new SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
                }
                jsch.addIdentity(sshKeyPath);
                System.out.println("使用 SSH Key 认证: " + sshKeyPath);
            } else if (password == null || password.trim().isEmpty()) {
                // 如果没有提供密码或密钥路径，则认证信息不全
                throw new SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
            }
            // 如果提供了密码，将在 getSession 后设置，因为 getSession 需要用户名、主机和端口先建立连接意图


            // 2. 获取 Session
            int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
            session = jsch.getSession(username, remoteHost, port);
            System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);


            // 如果使用密码认证且提供了密码
            if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
                session.setPassword(password);
                System.out.println("使用密码认证.");
            }

            // 设置连接不进行主机密钥检查 (生产环境不推荐，应该配置 known_hosts)
            // 在实际应用中，应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no"); // !!! 生产环境请谨慎使用或配置正确的主机密钥检查 !!!
            session.setConfig(config);

            // 3. 连接 Session
            session.connect();
            System.out.println("SFTP Session 连接成功.");

            // 4. 打开 SFTP Channel
            channel = session.openChannel("sftp");
            channel.connect();
            System.out.println("SFTP Channel 打开成功.");

            channelSftp = (ChannelSftp) channel;

            // 获取远程文件名和目录
            String remoteDir = remoteFilePath.substring(0, remoteFilePath.lastIndexOf('/'));
            String fileName = remoteFilePath.substring(remoteFilePath.lastIndexOf('/') + 1);

            // 切换目录并列出内容用于调试
            try {
                channelSftp.cd(remoteDir);
            } catch (SftpException e) {
                throw new SftpUploadException("切换远程目录失败: " + remoteDir, e);
            }

            // 列出目录内容用于调试
            Vector<ChannelSftp.LsEntry> entries = channelSftp.ls(".");
            List<String> fileNames = entries.stream()
                    .map(entry -> entry.getFilename())
                    .filter(name -> !name.equals(".") && !name.equals(".."))
                    .toList();

            System.out.println("远程目录中的文件列表: " + fileNames);

            // 尝试获取文件流
            try {
                inputStream = channelSftp.get(fileName);
            } catch (SftpException e) {
                throw new SftpUploadException("获取远程文件流失败: " + fileName, e);
            }

            if (inputStream == null) {
                throw new SftpUploadException("无法获取远程文件输入流，请检查文件是否存在或被其他进程占用: " + fileName);
            }
            ServletOutputStream outputStream = response.getOutputStream();
            IOUtils.copy(inputStream,outputStream);
            outputStream.flush();
        } catch (JSchException e) {
            throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
        } catch (SftpException e) {
            throw new SftpUploadException("SFTP 操作失败 (如切换目录或上传文件): " + e.getMessage(), e);
        } catch (SftpUploadException e) {
            // 重新抛出自定义异常
            throw e;
        } catch (Exception e) {
            // 捕获其他未知异常
            throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
        } finally {
            // 9. 关闭资源 (确保在任何情况下都关闭)
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    System.err.println("关闭本地文件流失败: " + e.getMessage());
                    e.printStackTrace(); // 打印堆栈以便调试
                }
            }
            if (channelSftp != null) {
                channelSftp.disconnect();
                System.out.println("SFTP Channel 已断开.");
            }
            if (channel != null) {
                channel.disconnect();
                System.out.println("SFTP Channel 资源已释放.");
            }
            if (session != null) {
                session.disconnect();
                System.out.println("SFTP Session 已断开.");
            }
        }
    }



    /**
     * 辅助方法：递归创建远程目录
     * @param channelSftp ChannelSftp 实例
     * @param remoteDir 要创建的目录路径
     * @throws SftpException 如果创建失败
     */
    private static void createRemoteDirRecursive(ChannelSftp channelSftp, String remoteDir) throws SftpException {
        // 标准化路径，去掉末尾的 /
        String cleanRemoteDir = remoteDir.endsWith("/") ? remoteDir.substring(0, remoteDir.length() - 1) : remoteDir;

        String[] pathElements = cleanRemoteDir.split("/");
        StringBuilder currentDir = new StringBuilder();
        try {
            channelSftp.cd("/"); // 先回到根目录
        } catch (SftpException e) {
            // 理论上不应该失败，除非根目录都不可访问
            throw new ServiceException();
        }

        for (String dir : pathElements) {
            if (dir == null || dir.isEmpty()) {
                continue; // 跳过空的路径元素，比如路径以/开头
            }
            currentDir.append("/").append(dir);
            try {
                channelSftp.cd(currentDir.toString());
            } catch (SftpException e) {
                if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
                    // 目录不存在，创建它
                    try {
                        System.out.println("创建目录: " + currentDir.toString());
                        channelSftp.mkdir(currentDir.toString());
                        channelSftp.cd(currentDir.toString()); // 创建后进入该目录
                        System.out.println("目录创建成功并进入: " + currentDir.toString());
                    } catch (SftpException e2) {
                        throw new SftpException(e2.id, "无法创建远程目录: " + currentDir.toString(), e2);
                    }
                } else {
                    // 其他 SFTP 异常
                    throw new SftpException(e.id, "切换或检查远程目录失败: " + currentDir.toString(), e);
                }
            }
        }
    }


    /**
     * 从远程服务器下载文件并返回文件输入流，并设置AflInfo对象
     *
     * @param remoteHost      远程服务器地址
     * @param remotePort      远程服务器端口
     * @param username        远程服务器用户名
     * @param password        远程服务器密码
     * @param sshKeyPath      SSH密钥路径
     * @param remoteFilePath  远程文件路径
     * @return AflInfo对象，包含文件输入流和文件信息
     * @throws SftpUploadException 如果下载文件或设置AflInfo对象时发生错误
     */
    public static AflInfo downloadFileSftpForInputStreamAndSetAflInfo(String remoteHost,
                                                                      Integer remotePort,
                                                                      String username,
                                                                      String password,
                                                                      String sshKeyPath,
                                                                      String remoteFilePath) throws SftpUploadException {

        Session session = null;
        Channel channel = null;
        ChannelSftp channelSftp = null;
        InputStream inputStream = null;

        try {
            JSch jsch = new JSch();

            // 1. 添加身份认证信息 (密码或密钥)
            if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
                // 使用 SSH Key 认证
                File sshKeyFile = new File(sshKeyPath);
                if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
                    throw new SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
                }
                jsch.addIdentity(sshKeyPath);
                System.out.println("使用 SSH Key 认证: " + sshKeyPath);
            } else if (password == null || password.trim().isEmpty()) {
                // 如果没有提供密码或密钥路径，则认证信息不全
                throw new SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
            }
            // 如果提供了密码，将在 getSession 后设置，因为 getSession 需要用户名、主机和端口先建立连接意图


            // 2. 获取 Session
            int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
            session = jsch.getSession(username, remoteHost, port);
            System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);


            // 如果使用密码认证且提供了密码
            if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
                session.setPassword(password);
                System.out.println("使用密码认证.");
            }

            // 设置连接不进行主机密钥检查 (生产环境不推荐，应该配置 known_hosts)
            // 在实际应用中，应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no"); // !!! 生产环境请谨慎使用或配置正确的主机密钥检查 !!!
            session.setConfig(config);

            // 3. 连接 Session
            session.connect();
            System.out.println("SFTP Session 连接成功.");

            // 4. 打开 SFTP Channel
            channel = session.openChannel("sftp");
            channel.connect();
            System.out.println("SFTP Channel 打开成功.");

            channelSftp = (ChannelSftp) channel;

            // 获取远程文件名和目录
            String remoteDir = remoteFilePath.substring(0, remoteFilePath.lastIndexOf('/'));
            String fileName = remoteFilePath.substring(remoteFilePath.lastIndexOf('/') + 1);
            String chmodDir = remoteFilePath.substring(0, remoteFilePath.indexOf("default"));
            log.info("分配权限路径,{}",chmodDir);
            // 切换目录并列出内容用于调试
            sudoChmodORwx(session,chmodDir,password);
            try {
                channelSftp.cd(remoteDir);
            } catch (SftpException e) {
                throw new SftpUploadException("切换远程目录失败: " + remoteDir, e);
            }

            // 列出目录内容用于调试
            Vector<ChannelSftp.LsEntry> entries = channelSftp.ls(".");
            List<String> fileNames = entries.stream()
                    .map(entry -> entry.getFilename())
                    .filter(name -> !name.equals(".") && !name.equals(".."))
                    .toList();

            System.out.println("远程目录中的文件列表: " + fileNames);

            // 尝试获取文件流
            try {
                inputStream = channelSftp.get(fileName);
            } catch (SftpException e) {
                throw new SftpUploadException("获取远程文件流失败: " + fileName, e);
            }

            if (inputStream == null) {
                throw new SftpUploadException("无法获取远程文件输入流，请检查文件是否存在或被其他进程占用: " + fileName);
            }

            //转字符串
            String fileContent = null;
            try {
                fileContent = IOUtils.toString(inputStream);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            //解析 key=value 内容
            Map<String, String> statsMap = new HashMap<>();
            for (String line : fileContent.split("\\r?\\n")) {
                if (line.contains(":")) {
                    String[] parts = line.split(":", 2);
                    statsMap.put(parts[0].trim(), parts[1].trim());
                }
            }

            return setAflinfo(statsMap);
        } catch (JSchException e) {
            throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
        } catch (SftpException e) {
            throw new SftpUploadException("SFTP 操作失败 (如切换目录或上传文件): " + e.getMessage(), e);
        } catch (SftpUploadException e) {
            // 重新抛出自定义异常
            throw e;
        } catch (Exception e) {
            // 捕获其他未知异常
            throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
        } finally {
            // 9. 关闭资源 (确保在任何情况下都关闭)
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    System.err.println("关闭本地文件流失败: " + e.getMessage());
                    e.printStackTrace(); // 打印堆栈以便调试
                }
            }
            if (channelSftp != null) {
                channelSftp.disconnect();
                System.out.println("SFTP Channel 已断开.");
            }
            if (channel != null) {
                channel.disconnect();
                System.out.println("SFTP Channel 资源已释放.");
            }
            if (session != null) {
                session.disconnect();
                System.out.println("SFTP Session 已断开.");
            }
        }

    }



    public static List<String> copyRemoteFile(String remoteHost,
                                      Integer remotePort,
                                      String username,
                                      String password,
                                      String sshKeyPath,
                                      List<String> sourceFilePaths,
                                      String targetDir) throws SftpUploadException {

        Session session = null;
        Channel channel = null;
        ChannelSftp channelSftp = null;
        InputStream inputStream = null;

        try {
            JSch jsch = new JSch();

            // 1. 添加身份认证信息 (密码或密钥)
            if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
                // 使用 SSH Key 认证
                File sshKeyFile = new File(sshKeyPath);
                if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
                    throw new SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
                }
                jsch.addIdentity(sshKeyPath);
                System.out.println("使用 SSH Key 认证: " + sshKeyPath);
            } else if (password == null || password.trim().isEmpty()) {
                // 如果没有提供密码或密钥路径，则认证信息不全
                throw new SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
            }
            // 如果提供了密码，将在 getSession 后设置，因为 getSession 需要用户名、主机和端口先建立连接意图


            // 2. 获取 Session
            int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
            session = jsch.getSession(username, remoteHost, port);
            System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);


            // 如果使用密码认证且提供了密码
            if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
                session.setPassword(password);
                System.out.println("使用密码认证.");
            }

            // 设置连接不进行主机密钥检查 (生产环境不推荐，应该配置 known_hosts)
            // 在实际应用中，应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no"); // !!! 生产环境请谨慎使用或配置正确的主机密钥检查 !!!
            session.setConfig(config);

            // 3. 连接 Session
            session.connect();
            System.out.println("SFTP Session 连接成功.");

            // 4. 打开 SFTP Channel
            channel = session.openChannel("sftp");
            channel.connect();
            System.out.println("SFTP Channel 打开成功.");

            channelSftp = (ChannelSftp) channel;

            // 创建目标目录（递归）
            createRemoteDirRecursive(channelSftp, targetDir);

            // 切换到目标目录
            channelSftp.cd(targetDir);

            List<String> copiedFiles = new ArrayList<>();

            for (String sourceFilePath : sourceFilePaths) {
                String chmodDir = sourceFilePath.substring(0, sourceFilePath.indexOf("default"));
                log.info("分配权限路径,{}",chmodDir);
                // 切换目录并列出内容用于调试
                sudoChmodORwx(session,chmodDir,password);

                // 获取源文件名
                String sourceFileName = sourceFilePath.substring(sourceFilePath.lastIndexOf('/') + 1);

                // 切换到源目录
                String sourceDir = sourceFilePath.substring(0, sourceFilePath.lastIndexOf('/'));
                channelSftp.cd(sourceDir);

                // 获取文件输入流
                inputStream = channelSftp.get(sourceFileName);

                // 复制文件
                channelSftp.put(inputStream, sourceFileName);

                System.out.println("文件复制成功: " + sourceFilePath + " -> " + targetDir + "/" + sourceFileName);
                copiedFiles.add(targetDir + "/" + sourceFileName);

                // 关闭输入流以释放资源
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        System.err.println("关闭本地文件流失败: " + e.getMessage());
                        e.printStackTrace(); // 打印堆栈以便调试
                    }
                    inputStream = null; // 重置 inputStream 以便下一个文件使用
                }
            }

            return copiedFiles;
        } catch (JSchException e) {
            throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
        } catch (SftpException e) {
            throw new SftpUploadException("SFTP 操作失败 (如切换目录或上传文件): " + e.getMessage(), e);
        } catch (SftpUploadException e) {
            // 重新抛出自定义异常
            throw e;
        } catch (Exception e) {
            // 捕获其他未知异常
            throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
        } finally {
            // 9. 关闭资源 (确保在任何情况下都关闭)
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    System.err.println("关闭本地文件流失败: " + e.getMessage());
                    e.printStackTrace(); // 打印堆栈以便调试
                }
            }
            if (channelSftp != null) {
                channelSftp.disconnect();
                System.out.println("SFTP Channel 已断开.");
            }
            if (channel != null) {
                channel.disconnect();
                System.out.println("SFTP Channel 资源已释放.");
            }
            if (session != null) {
                session.disconnect();
                System.out.println("SFTP Session 已断开.");
            }
        }
    }


    public static List<String> listFilesInRemoteDirectory( String remoteHost,
                                                           Integer remotePort,
                                                           String username,
                                                           String password,
                                                           String sshKeyPath,
                                                           String remoteDir) throws SftpUploadException {

        Session session = null;
        Channel channel = null;
        ChannelSftp channelSftp = null;

        try {
            JSch jsch = new JSch();

            // 1. 添加身份认证信息 (密码或密钥)
            if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
                // 使用 SSH Key 认证
                File sshKeyFile = new File(sshKeyPath);
                if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
                    throw new SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
                }
                jsch.addIdentity(sshKeyPath);
                System.out.println("使用 SSH Key 认证: " + sshKeyPath);
            } else if (password == null || password.trim().isEmpty()) {
                // 如果没有提供密码或密钥路径，则认证信息不全
                throw new SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
            }
            // 如果提供了密码，将在 getSession 后设置，因为 getSession 需要用户名、主机和端口先建立连接意图


            // 2. 获取 Session
            int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
            session = jsch.getSession(username, remoteHost, port);
            System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);


            // 如果使用密码认证且提供了密码
            if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
                session.setPassword(password);
                System.out.println("使用密码认证.");
            }

            // 设置连接不进行主机密钥检查 (生产环境不推荐，应该配置 known_hosts)
            // 在实际应用中，应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no"); // !!! 生产环境请谨慎使用或配置正确的主机密钥检查 !!!
            session.setConfig(config);

            // 3. 连接 Session
            session.connect();
            System.out.println("SFTP Session 连接成功.");

            // 4. 打开 SFTP Channel
            channel = session.openChannel("sftp");
            channel.connect();
            System.out.println("SFTP Channel 打开成功.");

            channelSftp = (ChannelSftp) channel;

            if (remoteDir.contains("default")){
                String chmodDir = remoteDir.substring(0, remoteDir.indexOf("default"));
                log.info("分配权限路径,{}",chmodDir);
                sudoChmodORwx(session,chmodDir,password);
            } else {
                log.warn("路径中未找到 'default'，跳过权限设置。当前路径: {}", remoteDir);
            }

            // 切换目录并列出内容用于调试

            // 切换到目标目录
            channelSftp.cd(remoteDir);

            // 列出文件
            List<String> fileNames = new ArrayList<>();

            Vector<ChannelSftp.LsEntry> entries = channelSftp.ls(".");
            if (!ObjectUtils.isEmpty(entries)){
                for (ChannelSftp.LsEntry entry : entries) {
                    String name = entry.getFilename();
                    if (!name.equals(".") && !name.equals("..")) {
                        fileNames.add(remoteDir + name); // 返回完整路径
                    }
                }
            }
            return fileNames;
        } catch (JSchException e) {
            log.error("SFTP 连接或认证失败: " ,e);
            throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
        } catch (SftpException e) {
            throw new SftpUploadException("SFTP 操作失败 (如切换目录或上传文件): " + e.getMessage(), e);
        } catch (SftpUploadException e) {
            // 重新抛出自定义异常
            throw e;
        } catch (Exception e) {
            // 捕获其他未知异常
            throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
        } finally {
            if (channelSftp != null) {
                channelSftp.disconnect();
                System.out.println("SFTP Channel 已断开.");
            }
            if (channel != null) {
                channel.disconnect();
                System.out.println("SFTP Channel 资源已释放.");
            }
            if (session != null) {
                session.disconnect();
                System.out.println("SFTP Session 已断开.");
            }
        }
    }

    public static void clearRemoteDirectoryFiles( String remoteHost,
                                                  Integer remotePort,
                                                  String username,
                                                  String password,
                                                  String sshKeyPath,
                                                  String remoteDir) throws SftpUploadException {

        Session session = null;
        Channel channel = null;
        ChannelSftp channelSftp = null;

        try {
            JSch jsch = new JSch();

            // 1. 添加身份认证信息 (密码或密钥)
            if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
                // 使用 SSH Key 认证
                File sshKeyFile = new File(sshKeyPath);
                if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
                    throw new SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
                }
                jsch.addIdentity(sshKeyPath);
                System.out.println("使用 SSH Key 认证: " + sshKeyPath);
            } else if (password == null || password.trim().isEmpty()) {
                // 如果没有提供密码或密钥路径，则认证信息不全
                throw new SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
            }
            // 如果提供了密码，将在 getSession 后设置，因为 getSession 需要用户名、主机和端口先建立连接意图


            // 2. 获取 Session
            int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
            session = jsch.getSession(username, remoteHost, port);
            System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);


            // 如果使用密码认证且提供了密码
            if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
                session.setPassword(password);
                System.out.println("使用密码认证.");
            }

            // 设置连接不进行主机密钥检查 (生产环境不推荐，应该配置 known_hosts)
            // 在实际应用中，应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no"); // !!! 生产环境请谨慎使用或配置正确的主机密钥检查 !!!
            session.setConfig(config);

            // 3. 连接 Session
            session.connect();
            System.out.println("SFTP Session 连接成功.");

            // 4. 打开 SFTP Channel
            channel = session.openChannel("sftp");
            channel.connect();
            System.out.println("SFTP Channel 打开成功.");

            channelSftp = (ChannelSftp) channel;

            String chmodDir = remoteDir.substring(0, remoteDir.indexOf("default"));
            log.info("分配权限路径,{}",chmodDir);
            // 切换目录并列出内容用于调试
            sudoChmodORwx(session,chmodDir,password);

            // 切换到目标目录
            channelSftp.cd(remoteDir);

            // 列出目录下的所有条目
            Vector<ChannelSftp.LsEntry> lsEntriesVector = channelSftp.ls(".");

            // 转换为 List 并过滤文件（排除 . 和 ..）
            List<ChannelSftp.LsEntry> entries = new ArrayList<>();
            for (ChannelSftp.LsEntry entry : lsEntriesVector) {
                String name = entry.getFilename();
                if (!name.equals(".") && !name.equals("..")) {
                    entries.add(entry);
                }
            }

            // 遍历并删除所有文件（不包括子目录）
            for (ChannelSftp.LsEntry entry : entries) {
                String name = entry.getFilename();
                String fullPath = remoteDir + "/" + name;

                // 检查是否是文件（非目录）
                if (!thisDirectory(channelSftp, fullPath)) {
                    channelSftp.rm(fullPath); // 删除文件
                    System.out.println("已删除文件: " + fullPath);
                }
            }
        } catch (JSchException e) {
            throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
        } catch (SftpException e) {
            throw new SftpUploadException("SFTP 操作失败 (如切换目录或上传文件): " + e.getMessage(), e);
        } catch (SftpUploadException e) {
            // 重新抛出自定义异常
            throw e;
        } catch (Exception e) {
            // 捕获其他未知异常
            throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
        } finally {
            if (channelSftp != null) {
                channelSftp.disconnect();
                System.out.println("SFTP Channel 已断开.");
            }
            if (channel != null) {
                channel.disconnect();
                System.out.println("SFTP Channel 资源已释放.");
            }
            if (session != null) {
                session.disconnect();
                System.out.println("SFTP Session 已断开.");
            }
        }
    }


    public static List<String> findCreateTimeByCommand(String remoteHost,
                                                       Integer remotePort,
                                                       String username,
                                                       String password,
                                                       String sshKeyPath,
                                                       String remoteDir) throws SftpUploadException {

        Session session = null;

        try {
            JSch jsch = new JSch();

            // 1. 添加身份认证信息 (密码或密钥)
            if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
                // 使用 SSH Key 认证
                File sshKeyFile = new File(sshKeyPath);
                if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
                    throw new SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
                }
                jsch.addIdentity(sshKeyPath);
                System.out.println("使用 SSH Key 认证: " + sshKeyPath);
            } else if (password == null || password.trim().isEmpty()) {
                // 如果没有提供密码或密钥路径，则认证信息不全
                throw new SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
            }
            // 如果提供了密码，将在 getSession 后设置，因为 getSession 需要用户名、主机和端口先建立连接意图


            // 2. 获取 Session
            int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
            session = jsch.getSession(username, remoteHost, port);
            System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);


            // 如果使用密码认证且提供了密码
            if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
                session.setPassword(password);
                System.out.println("使用密码认证.");
            }

            // 设置连接不进行主机密钥检查 (生产环境不推荐，应该配置 known_hosts)
            // 在实际应用中，应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no"); // !!! 生产环境请谨慎使用或配置正确的主机密钥检查 !!!
            session.setConfig(config);

            // 3. 连接 Session
            session.connect();
            System.out.println("SFTP Session 连接成功.");

            String chmodDir = remoteDir.substring(0, remoteDir.indexOf("default"));
            log.info("分配权限路径,{}",chmodDir);
            // 切换目录并列出内容用于调试
            sudoChmodORwx(session,chmodDir,password);

            // 开始执行 find 命令
            ChannelExec execChannel = (ChannelExec) session.openChannel("exec");

            // 设置要执行的命令
            String command = "find " + remoteDir + " -maxdepth 1 -type f -exec stat -c \"%n 创建时间: %w\" {} \\;";
            execChannel.setCommand(command);

            InputStream in = execChannel.getInputStream();
            execChannel.connect();

            byte[] tmp = new byte[1024];
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

            while (true) {
                while (in.available() > 0) {
                    int i = in.read(tmp, 0, 1024);
                    if (i < 0) break;
                    outputStream.write(tmp, 0, i);
                }

                if (execChannel.isClosed()) {
                    if (in.available() > 0) continue;
                    break;
                }

                try {
                    Thread.sleep(1000);
                } catch (Exception ignored) {}
            }

            // 转成字符串并按行拆分
            if (outputStream.size() > 0){
                String outputStr = outputStream.toString();
                String[] lines = outputStr.split("\\r?\\n");

                // 添加进 list
                List<String> result = new ArrayList<>(Arrays.asList(lines));
                execChannel.disconnect();

                return result;
            }else {
                return new ArrayList<>();
            }
        } catch (JSchException e) {
            throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
        } catch (SftpUploadException e) {
            // 重新抛出自定义异常
            throw e;
        } catch (Exception e) {
            // 捕获其他未知异常
            throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
        } finally {
            if (session != null) {
                session.disconnect();
                System.out.println("SFTP Session 已断开.");
            }
        }

    }



    public static List<String> findFileByteByCommand(String remoteHost,
                                                       Integer remotePort,
                                                       String username,
                                                       String password,
                                                       String sshKeyPath,
                                                       String remoteDir) throws SftpUploadException {

        Session session = null;

        try {
            JSch jsch = new JSch();

            // 1. 添加身份认证信息 (密码或密钥)
            if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
                // 使用 SSH Key 认证
                File sshKeyFile = new File(sshKeyPath);
                if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
                    throw new SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
                }
                jsch.addIdentity(sshKeyPath);
                System.out.println("使用 SSH Key 认证: " + sshKeyPath);
            } else if (password == null || password.trim().isEmpty()) {
                // 如果没有提供密码或密钥路径，则认证信息不全
                throw new SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
            }
            // 如果提供了密码，将在 getSession 后设置，因为 getSession 需要用户名、主机和端口先建立连接意图


            // 2. 获取 Session
            int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
            session = jsch.getSession(username, remoteHost, port);
            System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);


            // 如果使用密码认证且提供了密码
            if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
                session.setPassword(password);
                System.out.println("使用密码认证.");
            }

            // 设置连接不进行主机密钥检查 (生产环境不推荐，应该配置 known_hosts)
            // 在实际应用中，应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no"); // !!! 生产环境请谨慎使用或配置正确的主机密钥检查 !!!
            session.setConfig(config);

            // 3. 连接 Session
            session.connect();
            System.out.println("SFTP Session 连接成功.");

            String chmodDir = remoteDir.substring(0, remoteDir.indexOf("default"));
            log.info("分配权限路径,{}",chmodDir);
            // 切换目录并列出内容用于调试
            sudoChmodORwx(session,chmodDir,password);

            // 开始执行 find 命令
            ChannelExec execChannel = (ChannelExec) session.openChannel("exec");

            // 设置要执行的命令
            String command = "find " + remoteDir + " -maxdepth 1 -type f -exec stat -c \"%n %s bytes\" {} \\;";
            execChannel.setCommand(command);

            InputStream in = execChannel.getInputStream();
            execChannel.connect();

            byte[] tmp = new byte[1024];
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

            while (true) {
                while (in.available() > 0) {
                    int i = in.read(tmp, 0, 1024);
                    if (i < 0) break;
                    outputStream.write(tmp, 0, i);
                }

                if (execChannel.isClosed()) {
                    if (in.available() > 0) continue;
                    break;
                }

                try {
                    Thread.sleep(1000);
                } catch (Exception ignored) {}
            }

            // 转成字符串并按行拆分
            if (outputStream.size() > 0){
                String outputStr = outputStream.toString();
                String[] lines = outputStr.split("\\r?\\n");

                // 添加进 list
                List<String> result = new ArrayList<>(Arrays.asList(lines));
                execChannel.disconnect();

                return result;
            }else {
                return new ArrayList<>();
            }
        } catch (JSchException e) {
            throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
        } catch (SftpUploadException e) {
            // 重新抛出自定义异常
            throw e;
        } catch (Exception e) {
            // 捕获其他未知异常
            throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
        } finally {
            if (session != null) {
                session.disconnect();
                System.out.println("SFTP Session 已断开.");
            }
        }

    }

    public static List<String> copyRemoteFilesToLocal(String remoteHost,
                                                      Integer remotePort,
                                                      String username,
                                                      String password,
                                                      String sshKeyPath,
                                                      List<String> remoteFilePaths,
                                                      String localDir) throws SftpUploadException {

        List<String> copiedLocalPaths = new ArrayList<>();

        Session session = null;
        Channel channel = null;
        ChannelSftp channelSftp = null;
        InputStream inputStream = null;

        try {
            JSch jsch = new JSch();

            // 1. 添加身份认证信息 (密码或密钥)
            if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
                // 使用 SSH Key 认证
                File sshKeyFile = new File(sshKeyPath);
                if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
                    throw new SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
                }
                jsch.addIdentity(sshKeyPath);
                System.out.println("使用 SSH Key 认证: " + sshKeyPath);
            } else if (password == null || password.trim().isEmpty()) {
                // 如果没有提供密码或密钥路径，则认证信息不全
                throw new SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
            }
            // 如果提供了密码，将在 getSession 后设置，因为 getSession 需要用户名、主机和端口先建立连接意图


            // 2. 获取 Session
            int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
            session = jsch.getSession(username, remoteHost, port);
            System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);


            // 如果使用密码认证且提供了密码
            if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
                session.setPassword(password);
                System.out.println("使用密码认证.");
            }

            // 设置连接不进行主机密钥检查 (生产环境不推荐，应该配置 known_hosts)
            // 在实际应用中，应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no"); // !!! 生产环境请谨慎使用或配置正确的主机密钥检查 !!!
            session.setConfig(config);

            // 3. 连接 Session
            session.connect();
            System.out.println("SFTP Session 连接成功.");

            // 4. 打开 SFTP Channel
            channel = session.openChannel("sftp");
            channel.connect();
            System.out.println("SFTP Channel 打开成功.");

            channelSftp = (ChannelSftp) channel;

            // 创建本地目录
            File localDirectory = new File(localDir);
            if (!localDirectory.exists()) {
                localDirectory.mkdirs();
            }

            // 遍历远程文件路径并下载
            for (String remoteFile : remoteFilePaths) {
                String chmodDir = remoteFile.substring(0, remoteFile.indexOf("default"));
                log.info("分配权限路径,{}",chmodDir);
                // 切换目录并列出内容用于调试
                sudoChmodORwx(session,chmodDir,password);

                String fileName = new File(remoteFile).getName();
                String localFilePath = localDir + fileName;

                try (InputStream in = channelSftp.get(remoteFile);
                     FileOutputStream out = new FileOutputStream(localFilePath)) {
                    byte[] buffer = new byte[1024];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) > 0) {
                        out.write(buffer, 0, bytesRead);
                    }
                    copiedLocalPaths.add(localFilePath); // 添加本地路径
                } catch (Exception e) {
                    throw new RuntimeException("文件复制失败: " + remoteFile, e);
                }
            }
            return copiedLocalPaths;
        } catch (JSchException e) {
            throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
        } catch (SftpUploadException e) {
            // 重新抛出自定义异常
            throw e;
        } catch (Exception e) {
            // 捕获其他未知异常
            throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
        } finally {
            // 9. 关闭资源 (确保在任何情况下都关闭)
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    System.err.println("关闭本地文件流失败: " + e.getMessage());
                    e.printStackTrace(); // 打印堆栈以便调试
                }
            }
            if (channelSftp != null) {
                channelSftp.disconnect();
                System.out.println("SFTP Channel 已断开.");
            }
            if (channel != null) {
                channel.disconnect();
                System.out.println("SFTP Channel 资源已释放.");
            }
            if (session != null) {
                session.disconnect();
                System.out.println("SFTP Session 已断开.");
            }
        }
    }


    public static Map<String,String> copyRemoteFilesToLocalMap(String remoteHost,
                                                            Integer remotePort,
                                                            String username,
                                                            String password,
                                                            String sshKeyPath,
                                                            List<String> remoteFilePaths,
                                                            String localDir) throws SftpUploadException {

        Map<String,String> copiedLocalPaths = new HashMap<>();

        Session session = null;
        Channel channel = null;
        ChannelSftp channelSftp = null;
        InputStream inputStream = null;

        try {
            JSch jsch = new JSch();

            // 1. 添加身份认证信息 (密码或密钥)
            if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
                // 使用 SSH Key 认证
                File sshKeyFile = new File(sshKeyPath);
                if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
                    throw new SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
                }
                jsch.addIdentity(sshKeyPath);
                System.out.println("使用 SSH Key 认证: " + sshKeyPath);
            } else if (password == null || password.trim().isEmpty()) {
                // 如果没有提供密码或密钥路径，则认证信息不全
                throw new SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
            }
            // 如果提供了密码，将在 getSession 后设置，因为 getSession 需要用户名、主机和端口先建立连接意图


            // 2. 获取 Session
            int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
            session = jsch.getSession(username, remoteHost, port);
            System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);


            // 如果使用密码认证且提供了密码
            if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
                session.setPassword(password);
                System.out.println("使用密码认证.");
            }

            // 设置连接不进行主机密钥检查 (生产环境不推荐，应该配置 known_hosts)
            // 在实际应用中，应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no"); // !!! 生产环境请谨慎使用或配置正确的主机密钥检查 !!!
            session.setConfig(config);

            // 3. 连接 Session
            session.connect();
            System.out.println("SFTP Session 连接成功.");

            // 4. 打开 SFTP Channel
            channel = session.openChannel("sftp");
            channel.connect();
            System.out.println("SFTP Channel 打开成功.");

            channelSftp = (ChannelSftp) channel;

            // 创建本地目录
            File localDirectory = new File(localDir);
            if (!localDirectory.exists()) {
                localDirectory.mkdirs();
            }

            // 遍历远程文件路径并下载
            for (String remoteFile : remoteFilePaths) {
                String chmodDir = remoteFile.substring(0, remoteFile.indexOf("default"));
                log.info("分配权限路径,{}",chmodDir);
                // 切换目录并列出内容用于调试
                sudoChmodORwx(session,chmodDir,password);

                String fileName = new File(remoteFile).getName();
                String localFilePath = localDir + fileName;

                try (InputStream in = channelSftp.get(remoteFile);
                     FileOutputStream out = new FileOutputStream(localFilePath)) {
                    byte[] buffer = new byte[1024];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) > 0) {
                        out.write(buffer, 0, bytesRead);
                    }
                    copiedLocalPaths.put(fileName,localFilePath); // 添加本地路径
                } catch (Exception e) {
                    throw new RuntimeException("文件复制失败: " + remoteFile, e);
                }
            }
            return copiedLocalPaths;
        } catch (JSchException e) {
            throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
        } catch (SftpUploadException e) {
            // 重新抛出自定义异常
            throw e;
        } catch (Exception e) {
            // 捕获其他未知异常
            throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
        } finally {
            // 9. 关闭资源 (确保在任何情况下都关闭)
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    System.err.println("关闭本地文件流失败: " + e.getMessage());
                    e.printStackTrace(); // 打印堆栈以便调试
                }
            }
            if (channelSftp != null) {
                channelSftp.disconnect();
                System.out.println("SFTP Channel 已断开.");
            }
            if (channel != null) {
                channel.disconnect();
                System.out.println("SFTP Channel 资源已释放.");
            }
            if (session != null) {
                session.disconnect();
                System.out.println("SFTP Session 已断开.");
            }
        }
    }


    /**
     * 从远程服务器下载文件并直接返回文件的二进制流
     *
     * @param remoteHost      远程服务器地址
     * @param remotePort      远程服务器端口
     * @param username        远程服务器用户名
     * @param password        远程服务器密码
     * @param sshKeyPath      SSH密钥路径
     * @param remoteFilePaths 远程文件路径列表
     * @return Map<String, byte[]> 文件名和对应的二进制流
     * @throws SftpUploadException 如果下载文件过程中发生错误
     */
    public static Map<String, byte[]> copyRemoteFilesToByteArray(String remoteHost,
                                                                 Integer remotePort,
                                                                 String username,
                                                                 String password,
                                                                 String sshKeyPath,
                                                                 List<String> remoteFilePaths) throws SftpUploadException {

        Map<String, byte[]> fileByteArrays = new HashMap<>();

        Session session = null;
        Channel channel = null;
        ChannelSftp channelSftp = null;

        try {
            JSch jsch = new JSch();

            // 1. 添加身份认证信息 (密码或密钥)
            if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
                // 使用 SSH Key 认证
                File sshKeyFile = new File(sshKeyPath);
                if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
                    throw new SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
                }
                jsch.addIdentity(sshKeyPath);
                System.out.println("使用 SSH Key 认证: " + sshKeyPath);
            } else if (password == null || password.trim().isEmpty()) {
                // 如果没有提供密码或密钥路径，则认证信息不全
                throw new SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
            }

            // 2. 获取 Session
            int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
            session = jsch.getSession(username, remoteHost, port);
            System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);

            // 如果使用密码认证且提供了密码
            if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
                session.setPassword(password);
                System.out.println("使用密码认证.");
            }

            // 设置连接不进行主机密钥检查
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);

            // 3. 连接 Session
            session.connect();
            System.out.println("SFTP Session 连接成功.");

            // 4. 打开 SFTP Channel
            channel = session.openChannel("sftp");
            channel.connect();
            System.out.println("SFTP Channel 打开成功.");

            channelSftp = (ChannelSftp) channel;


            String chmodDir = remoteFilePaths.get(0).substring(0, remoteFilePaths.get(0).indexOf("default"));
            log.info("分配权限路径,{}", chmodDir);
            // 切换目录并列出内容用于调试
            sudoChmodORwx(session, chmodDir, password);

            // 遍历远程文件路径并下载
            for (String remoteFile : remoteFilePaths) {
                try (InputStream in = channelSftp.get(remoteFile);
                     ByteArrayOutputStream out = new ByteArrayOutputStream()) {
                    byte[] buffer = new byte[1024];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) > 0) {
                        out.write(buffer, 0, bytesRead);
                    }
                    fileByteArrays.put(remoteFile, out.toByteArray()); // 添加文件名和二进制数据
                } catch (Exception e) {
                    throw new SftpUploadException("文件读取失败: " + remoteFile, e);
                }
            }
            return fileByteArrays;
        } catch (JSchException e) {
            throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
        } catch (SftpUploadException e) {
            // 重新抛出自定义异常
            throw e;
        } catch (Exception e) {
            // 捕获其他未知异常
            throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
        } finally {
            // 关闭资源 (确保在任何情况下都关闭)
            if (channelSftp != null) {
                channelSftp.disconnect();
                System.out.println("SFTP Channel 已断开.");
            }
            if (channel != null) {
                channel.disconnect();
                System.out.println("SFTP Channel 资源已释放.");
            }
            if (session != null) {
                session.disconnect();
                System.out.println("SFTP Session 已断开.");
            }
        }
    }


    /**
     * 从远程服务器下载文件并返回文件输入流，并设置AflInfo对象
     *
     * @param remoteHost      远程服务器地址
     * @param remotePort      远程服务器端口
     * @param username        远程服务器用户名
     * @param password        远程服务器密码
     * @param sshKeyPath      SSH密钥路径
     * @param remoteFilePath  远程文件路径
     * @return AflInfo对象，包含文件输入流和文件信息
     * @throws SftpUploadException 如果下载文件或设置AflInfo对象时发生错误
     */
    public static AflPlotInfo downloadFileSftpForLastLineAndSetAflPlotInfo(String remoteHost,
                                                                      Integer remotePort,
                                                                      String username,
                                                                      String password,
                                                                      String sshKeyPath,
                                                                      String remoteFilePath) throws SftpUploadException {

        Session session = null;
        Channel channel = null;
        ChannelSftp channelSftp = null;
        InputStream inputStream = null;

        try {
            JSch jsch = new JSch();

            // 1. 添加身份认证信息 (密码或密钥)
            if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
                // 使用 SSH Key 认证
                File sshKeyFile = new File(sshKeyPath);
                if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
                    throw new SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
                }
                jsch.addIdentity(sshKeyPath);
                System.out.println("使用 SSH Key 认证: " + sshKeyPath);
            } else if (password == null || password.trim().isEmpty()) {
                // 如果没有提供密码或密钥路径，则认证信息不全
                throw new SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
            }
            // 如果提供了密码，将在 getSession 后设置，因为 getSession 需要用户名、主机和端口先建立连接意图


            // 2. 获取 Session
            int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
            session = jsch.getSession(username, remoteHost, port);
            System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);


            // 如果使用密码认证且提供了密码
            if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
                session.setPassword(password);
                System.out.println("使用密码认证.");
            }

            // 设置连接不进行主机密钥检查 (生产环境不推荐，应该配置 known_hosts)
            // 在实际应用中，应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no"); // !!! 生产环境请谨慎使用或配置正确的主机密钥检查 !!!
            session.setConfig(config);

            // 3. 连接 Session
            session.connect();
            System.out.println("SFTP Session 连接成功.");

            // 4. 打开 SFTP Channel
            channel = session.openChannel("sftp");
            channel.connect();
            System.out.println("SFTP Channel 打开成功.");

            channelSftp = (ChannelSftp) channel;
            String chmodDir = remoteFilePath.substring(0, remoteFilePath.indexOf("default"));
            log.info("分配权限路径,{}",chmodDir);
            // 切换目录并列出内容用于调试
            sudoChmodORwx(session,chmodDir,password);

            String remoteDir = remoteFilePath.substring(0, remoteFilePath.lastIndexOf('/'));
            String fileName = remoteFilePath.substring(remoteFilePath.lastIndexOf('/') + 1);

            channelSftp.cd(remoteDir);
            inputStream = channelSftp.get(fileName);

            if (inputStream == null) {
                throw new SftpUploadException("无法获取远程文件输入流，请检查文件是否存在或被其他进程占用: " + fileName);
            }

            // 读取文件，找到最后一行
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            String lastLine = "";

            while ((line = reader.readLine()) != null) {
                if (!line.startsWith("#")) { // 忽略注释行
                    lastLine = line;
                }
            }

            if (lastLine.isEmpty()) {
                return new AflPlotInfo();
            }

            // 按逗号分割字段
            String[] values = lastLine.split(",");

            if (values.length < 15) {
                throw new SftpUploadException("文件格式不正确，字段数量不足");
            }

            // 构建 AflPlotInfo 对象
            AflPlotInfo aflPlotInfo = new AflPlotInfo();

            aflPlotInfo.setRelativeTime(trim(values[0]));
            aflPlotInfo.setCyclesDone(trim(values[1]));
            aflPlotInfo.setCurItem(trim(values[2]));
            aflPlotInfo.setCorpusCount(trim(values[3]));
            aflPlotInfo.setPendingTotal(trim(values[4]));
            aflPlotInfo.setPendingFavs(trim(values[5]));
            aflPlotInfo.setMapSize(trim(values[6]));
            aflPlotInfo.setSavedCrashes(trim(values[7]));
            aflPlotInfo.setSavedHangs(trim(values[8]));
            aflPlotInfo.setMaxDepth(trim(values[9]));
            aflPlotInfo.setExecsPerSec(trim(values[10]));
            aflPlotInfo.setTotalExecs(trim(values[11]));
            aflPlotInfo.setEdgesFound(trim(values[12]));
            aflPlotInfo.setTotalCrashes(trim(values[13]));
            aflPlotInfo.setServersCount(trim(values[14]));

            return aflPlotInfo;
        } catch (JSchException e) {
            throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
        } catch (SftpException e) {
            throw new SftpUploadException("SFTP 操作失败 (如切换目录或上传文件): " + e.getMessage(), e);
        } catch (SftpUploadException e) {
            // 重新抛出自定义异常
            throw e;
        } catch (Exception e) {
            // 捕获其他未知异常
            throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
        } finally {
            // 9. 关闭资源 (确保在任何情况下都关闭)
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    System.err.println("关闭本地文件流失败: " + e.getMessage());
                    e.printStackTrace(); // 打印堆栈以便调试
                }
            }
            if (channelSftp != null) {
                channelSftp.disconnect();
                System.out.println("SFTP Channel 已断开.");
            }
            if (channel != null) {
                channel.disconnect();
                System.out.println("SFTP Channel 资源已释放.");
            }
            if (session != null) {
                session.disconnect();
                System.out.println("SFTP Session 已断开.");
            }
        }

    }



    private static AflInfo setAflinfo(Map<String, String> statsMap) {
        //映射到 AflInfo 实体
        AflInfo aflInfo = new AflInfo();

        aflInfo.setStartTime(statsMap.get("start_time"));
        aflInfo.setLastUpdate(statsMap.get("last_update"));
        aflInfo.setRunTime(statsMap.get("run_time"));
        aflInfo.setFuzzerPid(statsMap.get("fuzzer_pid"));
        aflInfo.setCyclesDone(statsMap.get("cycles_done"));
        aflInfo.setCyclesWoFinds(statsMap.get("cycles_wo_finds"));
        aflInfo.setTimeWoFinds(statsMap.get("time_wo_finds"));
        aflInfo.setFuzzTime(statsMap.get("fuzz_time"));
        aflInfo.setCalibrationTime(statsMap.get("calibration_time"));
        aflInfo.setCmplogTime(statsMap.get("cmplog_time"));
        aflInfo.setSyncTime(statsMap.get("sync_time"));
        aflInfo.setTrimTime(statsMap.get("trim_time"));
        aflInfo.setExecsDone(statsMap.get("execs_done"));
        aflInfo.setExecsPerSec(statsMap.get("execs_per_sec"));
        aflInfo.setExecsPsLastMin(statsMap.get("execs_ps_last_min"));
        aflInfo.setCorpusCount(statsMap.get("corpus_count"));
        aflInfo.setCorpusFavored(statsMap.get("corpus_favored"));
        aflInfo.setCorpusFound(statsMap.get("corpus_found"));
        aflInfo.setCorpusImported(statsMap.get("corpus_imported"));
        aflInfo.setCorpusVariable(statsMap.get("corpus_variable"));
        aflInfo.setMaxDepth(statsMap.get("max_depth"));
        aflInfo.setCurItem(statsMap.get("cur_item"));
        aflInfo.setPendingFavs(statsMap.get("pending_favs"));
        aflInfo.setPendingTotal(statsMap.get("pending_total"));
        aflInfo.setStability(statsMap.get("stability"));
        aflInfo.setBitmapCvg(statsMap.get("bitmap_cvg"));
        aflInfo.setSavedCrashes(statsMap.get("saved_crashes"));
        aflInfo.setSavedHangs(statsMap.get("saved_hangs"));
        aflInfo.setTotalTmout(statsMap.get("total_tmout"));
        aflInfo.setLastFind(statsMap.get("last_find"));
        aflInfo.setLastCrash(statsMap.get("last_crash"));
        aflInfo.setLastHang(statsMap.get("last_hang"));
        aflInfo.setExecsSinceCrash(statsMap.get("execs_since_crash"));
        aflInfo.setExecTimeout(statsMap.get("exec_timeout"));
        aflInfo.setSlowestExecMs(statsMap.get("slowest_exec_ms"));
        aflInfo.setPeakRssMb(statsMap.get("peak_rss_mb"));
        aflInfo.setCpuAffinity(statsMap.get("cpu_affinity"));
        aflInfo.setEdgesFound(statsMap.get("edges_found"));
        aflInfo.setTotalEdges(statsMap.get("total_edges"));
        aflInfo.setVarByteCount(statsMap.get("var_byte_count"));
        aflInfo.setHavocExpansion(statsMap.get("havoc_expansion"));
        aflInfo.setAutoDictEntries(statsMap.get("auto_dict_entries"));
        aflInfo.setTestcacheSize(statsMap.get("testcache_size"));
        aflInfo.setTestcacheCount(statsMap.get("testcache_count"));
        aflInfo.setTestcacheEvict(statsMap.get("testcache_evict"));
        aflInfo.setAflBanner(statsMap.get("afl_banner"));
        aflInfo.setAflVersion(statsMap.get("afl_version"));
        aflInfo.setTargetMode(statsMap.get("target_mode"));
        aflInfo.setCommandLine(statsMap.get("command_line"));

        return aflInfo;
    }


    public static void sudoChmodORwx(Session session, String remotePath, String sudoPassword) throws Exception {
        String command = "sudo -S chmod -R o+rwx " + remotePath;
        ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
        channelExec.setCommand(command);

        OutputStream out = channelExec.getOutputStream();
        InputStream in = channelExec.getInputStream();
        channelExec.setErrStream(System.err);

        channelExec.connect();

        // 写入sudo密码
        out.write((sudoPassword + "\n").getBytes());
        out.flush();

        // 读取命令输出
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }

        // 等待命令执行完成
        while (!channelExec.isClosed()) {
            Thread.sleep(100);
        }
        int exitStatus = channelExec.getExitStatus();
        channelExec.disconnect();

        if (exitStatus != 0) {
            throw new RuntimeException("sudo chmod 执行失败，退出码: " + exitStatus);
        } else {
            System.out.println("sudo chmod -R o+rwx 执行成功！");
        }
    }
    private static boolean thisDirectory(ChannelSftp channelSftp, String fullPath) {
        try {
            SftpATTRS attrs = channelSftp.lstat(fullPath);
            return attrs.isDir();
        } catch (SftpException e) {
            return false;
        }
    }


        // 自定义异常类，用于封装上传过程中的错误
    public static class SftpUploadException extends Exception {
        public SftpUploadException(String message) {
            super(message);
        }
        public SftpUploadException(String message, Throwable cause) {
            super(message, cause);
        }
    }

    // 示例用法
    public static void main(String[] args) {
        // 使用密码认证示例
        try {
            System.out.println("--- 开始通过密码认证 SFTP 上传 ---");
            SftpUploadUtil.uploadFileViaSftp(
                    "your-remote-server-ip", // 替换为你的远程服务器IP或主机名
                    22,                         // SFTP端口 (如果不是22，请修改)
                    "your-username",            // 替换为你的 SFTP 用户名
                    "your-password",            // 替换为你的 SFTP 密码
                    null,                       // SSH Key 路径 (密码认证时为 null)
                    "path/to/your/local/file.txt", // 替换为你要上传的本地文件路径
                    "/upload/target/directory/", // 替换为远程服务器上的目标目录 (确保用户有写入权限)
                    "uploaded_file.txt"         // 上传到远程服务器的文件名 (null 或空则使用本地文件名)
            );
            System.out.println("文件通过密码认证 SFTP 上传成功!");
        } catch (SftpUploadException e) {
            System.err.println("文件通过密码认证 SFTP 上传失败: " + e.getMessage());
            e.printStackTrace();
        }

        System.out.println("\n------------------------------------\n");

        // 使用 SSH Key 认证示例 (假设你有一个私钥文件)
        // 请确保你的 SSH Key 不需要密码 (passphrase)
        try {
            System.out.println("--- 开始通过 SSH Key 认证 SFTP 上传 ---");
            SftpUploadUtil.uploadFileViaSftp(
                    "your-remote-server-ip", // 替换为你的远程服务器IP或主机名
                    null,                       // SFTP端口 (使用默认端口 22)
                    "your-username",            // 替换为你的 SFTP 用户名
                    null,                       // 密码 (SSH Key 认证时为 null)
                    "/path/to/your/private/key",// 替换为你的本地私钥文件路径 (例如: /home/user/.ssh/id_rsa)
                    "path/to/another/local/file.log", // 替换为另一个你要上传的本地文件路径
                    "/upload/other/files/",     // 替换为另一个远程目录
                    null                       // 远程文件名使用本地文件名
            );
            System.out.println("文件通过 SSH Key 认证 SFTP 上传成功!");
        } catch (SftpUploadException e) {
            System.err.println("文件通过 SSH Key 认证 SFTP 上传失败: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println("\n------------------------------------\n");
    }
}
