JSCH使用SFTP详细教程

1. JSCH和SFTP基础概念

1.1 什么是JSCH?

JSCH(Java Secure Channel)是一个纯Java实现的SSH2协议库,由JCraft开发。它提供了完整的SSH客户端功能,包括:

  • SFTP(SSH File Transfer Protocol):安全文件传输协议
  • SCP(Secure Copy Protocol):安全复制协议
  • SSH执行远程命令:在远程服务器执行命令
  • 端口转发:本地和远程端口转发
  • 公钥认证:支持各种公钥认证方式

1.2 SFTP协议特点

SFTP(SSH File Transfer Protocol)是基于SSH协议的文件传输协议,具有以下特点:

  • 安全性高:所有数据传输都经过加密
  • 跨平台:支持不同操作系统之间的文件传输
  • 功能丰富:支持文件上传、下载、删除、重命名等操作
  • 可靠性强:支持断点续传和完整性校验

1.3 JSCH的优势

  • 纯Java实现:无需额外的本地库依赖
  • 轻量级:jar包大小约280KB
  • 功能完整:支持SSH2协议的大部分功能
  • 开源免费:基于BSD许可证
  • 活跃维护:持续更新和维护

1.4 常用场景

// 常见的SFTP使用场景示例
public class SftpUseCases {
   
   
    
    // 1. 文件备份
    public void backupFiles() {
   
   
        // 将本地文件备份到远程服务器
    }
    
    // 2. 日志收集
    public void collectLogs() {
   
   
        // 从远程服务器下载日志文件
    }
    
    // 3. 数据同步
    public void syncData() {
   
   
        // 在不同服务器间同步数据文件
    }
    
    // 4. 部署文件
    public void deployApplication() {
   
   
        // 上传应用程序文件到服务器
    }
    
    // 5. 监控文件变化
    public void monitorFileChanges() {
   
   
        // 定期检查远程目录的文件变化
    }
}

2. 环境配置和依赖管理

2.1 Maven依赖配置

pom.xml文件中添加JSCH依赖:

<dependencies>
    <!-- JSCH核心依赖 -->
    <dependency>
        <groupId>com.jcraft</groupId>
        <artifactId>jsch</artifactId>
        <version>0.1.55</version>
    </dependency>
    
    <!-- 日志依赖(可选) -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.36</version>
    </dependency>
    
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.12</version>
    </dependency>
    
    <!-- Apache Commons IO(文件操作辅助) -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.11.0</version>
    </dependency>
    
    <!-- JSON处理(配置文件处理) -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.15.2</version>
    </dependency>
</dependencies>

2.2 Gradle依赖配置

build.gradle文件中添加依赖:

dependencies {
    // JSCH核心依赖
    implementation 'com.jcraft:jsch:0.1.55'
    
    // 日志依赖
    implementation 'org.slf4j:slf4j-api:1.7.36'
    implementation 'ch.qos.logback:logback-classic:1.2.12'
    
    // 工具类依赖
    implementation 'commons-io:commons-io:2.11.0'
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'
}

2.3 基础配置类

创建SFTP连接配置类:

package com.example.sftp.config;

import com.fasterxml.jackson.annotation.JsonProperty;

/**
 * SFTP连接配置类
 */
public class SftpConfig {
   
   
    
    @JsonProperty("host")
    private String host;
    
    @JsonProperty("port")
    private int port = 22;
    
    @JsonProperty("username")
    private String username;
    
    @JsonProperty("password")
    private String password;
    
    @JsonProperty("privateKeyPath")
    private String privateKeyPath;
    
    @JsonProperty("passphrase")
    private String passphrase;
    
    @JsonProperty("timeout")
    private int timeout = 30000; // 30秒
    
    @JsonProperty("strictHostKeyChecking")
    private boolean strictHostKeyChecking = false;
    
    @JsonProperty("preferredAuthentications")
    private String preferredAuthentications = "publickey,password";
    
    // 构造函数
    public SftpConfig() {
   
   }
    
    public SftpConfig(String host, String username, String password) {
   
   
        this.host = host;
        this.username = username;
        this.password = password;
    }
    
    public SftpConfig(String host, String username, String privateKeyPath, String passphrase) {
   
   
        this.host = host;
        this.username = username;
        this.privateKeyPath = privateKeyPath;
        this.passphrase = passphrase;
    }
    
    // Getters and Setters
    public String getHost() {
   
    return host; }
    public void setHost(String host) {
   
    this.host = host; }
    
    public int getPort() {
   
    return port; }
    public void setPort(int port) {
   
    this.port = port; }
    
    public String getUsername() {
   
    return username; }
    public void setUsername(String username) {
   
    this.username = username; }
    
    public String getPassword() {
   
    return password; }
    public void setPassword(String password) {
   
    this.password = password; }
    
    public String getPrivateKeyPath() {
   
    return privateKeyPath; }
    public void setPrivateKeyPath(String privateKeyPath) {
   
    this.privateKeyPath = privateKeyPath; }
    
    public String getPassphrase() {
   
    return passphrase; }
    public void setPassphrase(String passphrase) {
   
    this.passphrase = passphrase; }
    
    public int getTimeout() {
   
    return timeout; }
    public void setTimeout(int timeout) {
   
    this.timeout = timeout; }
    
    public boolean isStrictHostKeyChecking() {
   
    return strictHostKeyChecking; }
    public void setStrictHostKeyChecking(boolean strictHostKeyChecking) {
   
    
        this.strictHostKeyChecking = strictHostKeyChecking; 
    }
    
    public String getPreferredAuthentications() {
   
    return preferredAuthentications; }
    public void setPreferredAuthentications(String preferredAuthentications) {
   
    
        this.preferredAuthentications = preferredAuthentications; 
    }
    
    @Override
    public String toString() {
   
   
        return String.format("SftpConfig{host='%s', port=%d, username='%s', timeout=%d}", 
                           host, port, username, timeout);
    }
}

2.4 配置文件示例

创建配置文件sftp-config.json

{
   
   
  "host": "192.168.1.100",
  "port": 22,
  "username": "ftpuser",
  "password": "password123",
  "timeout": 30000,
  "strictHostKeyChecking": false,
  "preferredAuthentications": "password,publickey"
}

创建公钥认证配置文件sftp-config-key.json

{
   
   
  "host": "192.168.1.100",
  "port": 22,
  "username": "ftpuser",
  "privateKeyPath": "/path/to/private/key",
  "passphrase": "keypassphrase",
  "timeout": 30000,
  "strictHostKeyChecking": false,
  "preferredAuthentications": "publickey,password"
}

3. SFTP连接管理

3.1 基础连接类

创建SFTP连接管理类:

package com.example.sftp.core;

import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Properties;

/**
 * SFTP连接管理器
 */
public class SftpConnectionManager {
   
   
    
    private static final Logger logger = LoggerFactory.getLogger(SftpConnectionManager.class);
    
    private JSch jsch;
    private Session session;
    private ChannelSftp channelSftp;
    private SftpConfig config;
    
    public SftpConnectionManager(SftpConfig config) {
   
   
        this.config = config;
        this.jsch = new JSch();
    }
    
    /**
     * 建立SFTP连接
     */
    public void connect() throws JSchException {
   
   
        logger.info("正在连接SFTP服务器: {}", config.getHost());
        
        try {
   
   
            // 创建会话
            session = jsch.getSession(config.getUsername(), config.getHost(), config.getPort());
            
            // 设置密码认证
            if (config.getPassword() != null && !config.getPassword().isEmpty()) {
   
   
                session.setPassword(config.getPassword());
            }
            
            // 设置私钥认证
            if (config.getPrivateKeyPath() != null && !config.getPrivateKeyPath().isEmpty()) {
   
   
                if (config.getPassphrase() != null && !config.getPassphrase().isEmpty()) {
   
   
                    jsch.addIdentity(config.getPrivateKeyPath(), config.getPassphrase());
                } else {
   
   
                    jsch.addIdentity(config.getPrivateKeyPath());
                }
            }
            
            // 配置会话属性
            Properties properties = new Properties();
            properties.put("StrictHostKeyChecking", 
                          config.isStrictHostKeyChecking() ? "yes" : "no");
            properties.put("PreferredAuthentications", config.getPreferredAuthentications());
            session.setConfig(properties);
            
            // 设置超时时间
            session.setTimeout(config.getTimeout());
            
            // 建立连接
            session.connect();
            logger.info("成功连接到SFTP服务器");
            
            // 打开SFTP通道
            Channel channel = session.openChannel("sftp");
            channel.connect();
            channelSftp = (ChannelSftp) channel;
            
            logger.info("SFTP通道已建立");
            
        } catch (JSchException e) {
   
   
            logger.error("连接SFTP服务器失败: {}", e.getMessage());
            disconnect(); // 清理资源
            throw e;
        }
    }
    
    /**
     * 断开SFTP连接
     */
    public void disconnect() {
   
   
        if (channelSftp != null && channelSftp.isConnected()) {
   
   
            channelSftp.disconnect();
            logger.info("SFTP通道已断开");
        }
        
        if (session != null && session.isConnected()) {
   
   
            session.disconnect();
            logger.info("SSH会话已断开");
        }
    }
    
    /**
     * 检查连接状态
     */
    public boolean isConnected() {
   
   
        return session != null && session.isConnected() && 
               channelSftp != null && channelSftp.isConnected();
    }
    
    /**
     * 重新连接
     */
    public void reconnect() throws JSchException {
   
   
        logger.info("正在重新连接SFTP服务器...");
        disconnect();
        connect();
    }
    
    /**
     * 获取SFTP通道
     */
    public ChannelSftp getChannelSftp() {
   
   
        if (!isConnected()) {
   
   
            throw new IllegalStateException("SFTP连接未建立");
        }
        return channelSftp;
    }
    
    /**
     * 获取会话信息
     */
    public String getSessionInfo() {
   
   
        if (session != null) {
   
   
            return String.format("连接到 %s@%s:%d, 会话状态: %s", 
                               session.getUserName(), 
                               session.getHost(), 
                               session.getPort(),
                               session.isConnected() ? "已连接" : "未连接");
        }
        return "无会话信息";
    }
}

3.2 连接池管理

创建SFTP连接池:

package com.example.sftp.pool;

import com.example.sftp.core.SftpConnectionManager;
import com.example.sftp.config.SftpConfig;
import com.jcraft.jsch.JSchException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * SFTP连接池
 */
public class SftpConnectionPool {
   
   
    
    private static final Logger logger = LoggerFactory.getLogger(SftpConnectionPool.class);
    
    private final BlockingQueue<SftpConnectionManager> connectionPool;
    private final SftpConfig config;
    private final int maxConnections;
    private final AtomicInteger currentConnections = new AtomicInteger(0);
    private volatile boolean isShutdown = false;
    
    public SftpConnectionPool(SftpConfig config, int maxConnections) {
   
   
        this.config = config;
        this.maxConnections = maxConnections;
        this.connectionPool = new ArrayBlockingQueue<>(maxConnections);
        
        // 预创建一些连接
        initializePool();
    }
    
    /**
     * 初始化连接池
     */
    private void initializePool() {
   
   
        int initialConnections = Math.min(2, maxConnections);
        for (int i = 0; i < initialConnections; i++) {
   
   
            try {
   
   
                SftpConnectionManager connection = createConnection();
                connectionPool.offer(connection);
                currentConnections.incrementAndGet();
                logger.info("预创建SFTP连接 {}/{}", i + 1, initialConnections);
            } catch (Exception e) {
   
   
                logger.warn("预创建SFTP连接失败: {}", e.getMessage());
            }
        }
        logger.info("SFTP连接池初始化完成,当前连接数: {}", currentConnections.get());
    }
    
    /**
     * 创建新连接
     */
    private SftpConnectionManager createConnection() throws JSchException {
   
   
        SftpConnectionManager connection = new SftpConnectionManager(config);
        connection.connect();
        return connection;
    }
    
    /**
     * 获取连接
     */
    public SftpConnectionManager getConnection() throws JSchException, InterruptedException {
   
   
        return getConnection(30, TimeUnit.SECONDS);
    }
    
    /**
     * 获取连接(带超时)
     */
    public SftpConnectionManager getConnection(long timeout, TimeUnit unit) 
            throws JSchException, InterruptedException {
   
   
        
        if (isShutdown) {
   
   
            throw new IllegalStateException("连接池已关闭");
        }
        
        // 先尝试从池中获取
        SftpConnectionManager connection = connectionPool.poll();
        
        if (connection != null) {
   
   
            // 检查连接是否有效
            if (connection.isConnected()) {
   
   
                logger.debug("从连接池获取连接");
                return connection;
            } else {
   
   
                // 连接失效,尝试重连
                try {
   
   
                    connection.reconnect();
                    logger.debug("重新连接成功");
                    return connection;
                } catch (JSchException e) {
   
   
                    logger.warn("重新连接失败,创建新连接");
                    currentConnections.decrementAndGet();
                }
            }
        }
        
        // 如果池中没有可用连接且未达到最大连接数,创建新连接
        if (currentConnections.get() < maxConnections) {
   
   
            try {
   
   
                connection = createConnection();
                currentConnections.incrementAndGet();
                logger.debug("创建新的SFTP连接");
                return connection;
            } catch (JSchException e) {
   
   
                logger.error("创建新连接失败: {}", e.getMessage());
                throw e;
            }
        }
        
        // 等待可用连接
        connection = connectionPool.poll(timeout, unit);
        if (connection == null) {
   
   
            throw new RuntimeException("获取SFTP连接超时");
        }
        
        // 检查连接有效性
        if (!connection.isConnected()) {
   
   
            try {
   
   
                connection.reconnect();
            } catch (JSchException e) {
   
   
                currentConnections.decrementAndGet();
                throw e;
            }
        }
        
        return connection;
    }
    
    /**
     * 归还连接
     */
    public void returnConnection(SftpConnectionManager connection) {
   
   
        if (connection == null || isShutdown) {
   
   
            return;
        }
        
        if (connection.isConnected()) {
   
   
            boolean offered = connectionPool.offer(connection);
            if (!offered) {
   
   
                // 池已满,关闭连接
                connection.disconnect();
                currentConnections.decrementAndGet();
                logger.debug("连接池已满,关闭多余连接");
            } else {
   
   
                logger.debug("连接已归还到连接池");
            }
        } else {
   
   
            // 连接已断开
            currentConnections.decrementAndGet();
            logger.debug("归还无效连接,当前连接数: {}", currentConnections.get());
        }
    }
    
    /**
     * 关闭连接池
     */
    public void shutdown<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈凯哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值