直接上代码
pom.xml
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.0</version>
</dependency>
application.xml
sftp:
host: xxx.xxx.xxx.xxx
userName: xxxxxx
passWord: xxxxx
port: 22
timeout: 30000 # 设置超时时间为30秒
prvkeyPath: D:/key/xxxx_ssh
SftpConfig.java
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
这个注解很重要, 代码重构后启动报错MXBean already registered with name org.apache.commons.pool2:type=GenericObj
,加上这个注解后异常消失.
@Configuration
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
public class SftpConfig {
@Value("${sftp.host}")
private String sftpHost;
@Value("${sftp.userName}")
private String sftpUserName;
@Value("${sftp.passWord}")
private String sftpPassWord;
@Value("${sftp.port}")
private int sftpPort;
@Value("${sftp.timeout}")
private int sftpTimeout;
@Value("${sftp.prvkeyPath}")
private String prvkeyPath;
@Bean
public JSch jsch() {
return new JSch();
}
@Bean
public Session jschSession(JSch jsch) throws JSchException {
Session session = jsch.getSession(sftpUserName, sftpHost, sftpPort);
session.setConfig("StrictHostKeyChecking", "no");
session.setTimeout(sftpTimeout);
session.setPassword(""); // 不使用密码,使用密钥认证
jsch.addIdentity(prvkeyPath, sftpPassWord); // 添加私钥文件
return session;
}
@Bean
public GenericObjectPoolConfig<ChannelSftp> sftpPoolConfig() {
GenericObjectPoolConfig<ChannelSftp> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(10); // 最大连接数
poolConfig.setMaxIdle(5); // 最大空闲连接数
poolConfig.setMinIdle(1); // 最小空闲连接数
poolConfig.setMaxWait(Duration.ofMillis(10000)); // 最长等待时间(Duration对象)
// 其他连接池配置参数...
return poolConfig;
}
@Bean
public GenericObjectPool<ChannelSftp> sftpChannelPool(Session jschSession) {
GenericObjectPool<ChannelSftp> pool = new GenericObjectPool<>(new ChannelSftpFactory(jschSession));
return pool;
}
private static class ChannelSftpFactory extends BasePooledObjectFactory<ChannelSftp> {
private final Session session;
public ChannelSftpFactory(Session session) {
this.session = session;
}
@Override
public ChannelSftp create() throws Exception {
if (!session.isConnected()) {
session.connect();
}
ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp");
sftp.connect();
return sftp;
}
@Override
public PooledObject<ChannelSftp> wrap(ChannelSftp sftp) {
return new DefaultPooledObject<>(sftp);
}
}
}
SftpService.java
@Slf4j
@Component
public class SftpService{
@Autowired
private GenericObjectPool<ChannelSftp> sftpChannelPool;
public List<String> listFiles(String directory) {
ChannelSftp sftp = null;
try {
sftp = sftpChannelPool.borrowObject();
Vector files = sftp.ls(directory);
if (CollUtil.isNotEmpty(files)) {
List<String> list = new ArrayList<>(files.size());
for (Object object : files) {
ChannelSftp.LsEntry file = (ChannelSftp.LsEntry) object;
log.info("fileName:{}", file.getFilename());
// 文件名需包含后缀名以及排除 .和 ..
// boolean match = file.getFilename().contains(matchRule); // 取消文件名称匹配
if (!DOT.equals(file.getFilename()) && !DOUBLE_DOT.equals(file.getFilename())
&& file.getFilename().contains(DOT)) {
String newFilePath = directory.endsWith(SLASH) ? directory + file.getFilename() :
directory + SLASH + file.getFilename();
list.add(newFilePath);
}
}
return list;
}
} catch (Exception e) {
log.error("sftp list files error", e);
throw new FileSyncException("sftp list files error");
}finally {
if (sftp != null) {
try {
// 手动归还连接到连接池
sftpChannelPool.returnObject(sftp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return Collections.emptyList();
}
}
TestController.Java 测试
@RestController
@RequestMapping("/sftp")
public class TestController {
@Autowired
private SftpClient sftpClient;
@GetMapping("/listFiles")
public List<String> listFiles(@RequestParam String directory) {
return sftpClient.listFiles(directory);
}
}
以上仅提供参考, 代码还有很大优化空间.