# haibaracp-spring-boot-starter
**Repository Path**: noodle-framework/haibaracp-spring-boot-starter
## Basic Information
- **Project Name**: haibaracp-spring-boot-starter
- **Description**: SFTP文件传输的Spring Boot Starter,通过与Spring Data XxxTemplate模板类一样的 SftpTemplate 轻松完成文件传输。使用Commons-pool2作为连接池。
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: 2.x
- **Homepage**: https://github.com/hligaty/haibaracp-spring-boot-starter
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 4
- **Created**: 2023-05-17
- **Last Updated**: 2023-05-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
[English](README.md) | [中文](README_zh_CN.md)
# HaibaraCP
> **Github:[hligaty/haibaracp-spring-boot-starter: SFTP Connect Pool (github.com)](https://github.com/hligaty/haibaracp-spring-boot-starter)**
> **Gitee:[haibaracp-spring-boot-starter: SFTP Connect Pool (gitee.com)](https://gitee.com/hligy/haibaracp-spring-boot-starter)**
> **欢迎使用和Star支持,如使用过程中碰到问题,可以提出Issue,我会尽力完善**
## 介绍
HaibaraCP 是一个 SFTP 的 SpringBoot Starter,支持密码和密钥登录以及多个 Host 连接,并提供和 RedisTemplate 一样优雅的 SftpTemplate。SFTP 通过 SSH 建立连接,而 SSH 连接数默认是有限的,10 个以外的连接将有 30 % 的概率连接失败,当超过 100 个连接时将拒绝创建新连接,因此要避免频繁创建新连接。
## Maven 依赖
| spring boot version | haibaracp |
| :-----------------: | :-------: |
| 2.x.x | 1.2.3 |
| 3.x.x | 2.0.0 |
依赖 Apache commons-pool2:
```xml
io.github.hligaty
haibaracp-spring-boot-starter
x.x.x
org.apache.commons
commons-pool2
```
## 配置
详细的配置属性说明见开发工具的自动提示。
### 密码登录
```yml
sftp:
enabled-log: false
host: localhost
port: 22
username: root
password: 123456
kex: diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1,diffie-hellman-group-exchange-sha256
```
### 密钥登录
```yml
sftp:
enabled-log: false
host: localhost
port: 22
username: root
strict-host-key-checking: true
key-path: C:\\Users\\user\\.ssh\\id_rsa
password: Jui8cv@kK9!0
kex: diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1,diffie-hellman-group-exchange-sha256
```
### 多 Host
比如两个 Host,一个密码登录,一个密钥登录:
```yml
sftp:
enabled-log: false
hosts:
# 地址的名字,你可以通过它来切换连接
remote-1:
host: 127.0.0.1
port: 22
username: root
password: 123456
kex: diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1,diffie-hellman-group-exchange-sha256
local-1:
host: 127.0.0.1
port: 22
username: root
strict-host-key-checking: true
key-path: C:\\Users\\user\\.ssh\\id_rsa
password: Jui8cv@kK9!0
kex: diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1,diffie-hellman-group-exchange-sha256
```
### 连接池(可以不配置)
单 Host 连接池配置:
```yml
sftp:
pool:
min-idle: 1
max-idle: 8
max-active: 8
max-wait: -1
test-on-borrow: true
test-on-return: false
test-while-idle: true
time-between-eviction-runs: 600000
min-evictable-idle-time-millis: 1800000
```
多 Host 连接池配置:
```yml
sftp:
pool:
min-idle-per-key: 1
max-idle-per-key: 8
max-active-per-key: 8
max-active: 8
max-wait: -1
test-on-borrow: true
test-on-return: false
test-while-idle: true
time-between-eviction-runs: 600000
min-evictable-idle-time-millis: 1800000
```
## 用法
HaibaraCP 提供 SftpTemplate 类,它与 `spring-boot-starter-data-redis` 提供的 RedisTemplate 使用方法相同,任意方式注入即可使用:
```java
@Component
public class XXXService {
private final SftpTemplate sftpTemplate;
public XXXService(SftpTemplate sftpTemplate) {
this.sftpTemplate = sftpTemplate;
}
public void service(String from, OutputStream to) throws Exception {
sftpTemplate.download(from, to);
}
}
```
## API
- 所有方法都可能抛出 `SftpException`,这通常代表连接出问题了,也可能是你上传或下载的文件不存在。
- sftp 操作可能会改变工作目录,因此在连接返回给池前,框架会重置工作目录为原始目录。注意这只会重置远端工作路径,不会重置本地工作路径(通常你并不关心本地工作路径)。
下面的介绍全部使用 `配置` 章节中的配置进行说明,因此初始工作目录是 `/root`。
### upload
上传文件,该方法会递归创建上传的远程文件所在的父目录。
```java
// 上传 D:\\aptx4869.docx 到 /home/haibara/aptx4869.docx
sftpTemplate.upload("D:\\aptx4869.docx", "/home/haibara/aptx4869.docx");
// 上传 D:\\aptx4869.pdf 到 /root/haibara/aptx4869.pdf
sftpTemplate.upload("D:\\aptx4869.pdf", "haibara/aptx4869.pdf");
// 上传 D:\\aptx4869.doc 到 /root/aptx4869.doc
sftpTemplate.upload("D:\\aptx4869.doc", "aptx4869.doc");
```
### download
下载文件,该方法只会创建下载的本地文件,不会创建本地文件的父目录。
```java
// 下载 /home/haibara/aptx4869.docx 到 D:\\aptx4869.docx
sftpTemplate.download("/home/haibara/aptx4869.docx", "D:\\aptx4869.docx");
// 下载 /root/haibara/aptx4869.pdf 到 D:\\aptx4869.pdf
sftpTemplate.download("haibara/aptx4869.pdf", "D:\\aptx4869.pdf");
// 下载 /root/aptx4869.doc 到 D:\\aptx4869.doc
sftpTemplate.download("aptx4869.doc", "D:\\aptx4869.doc");
```
### exists
```java
// 测试 /home/haibara/aptx4869.docx 是否存在
boolean result1 = sftpTemplate.exists("/home/haibara/aptx4869.pdf");
// 测试 /root/haibara/aptx4869.docx 是否存在
boolean result2 = sftpTemplate.exists("haibara/aptx4869.docx");
// 测试 /root/aptx4869.docx 是否存在
boolean result3 = sftpTemplate.exists("aptx4869.doc");
```
### list
```java
// 查看文件 /home/haibara/aptx4869.pdf
LsEntry[] list1 = sftpTemplate.list("/home/haibara/aptx4869.pdf");
// 查看文件 /root/haibara/aptx4869.docx
LsEntry[] list2 = sftpTemplate.list("haibara/aptx4869.docx");
// 查看文件 /root/aptx4869.doc
LsEntry[] list3 = sftpTemplate.list("aptx4869.doc");
// 查看目录 /home/haibara
LsEntry[] list4 = sftpTemplate.list("/home/haibara");
// 查看目录 /root/haibara
LsEntry[] list5 = sftpTemplate.list("haibara");
```
### execute
`execute(SftpCallback action)` 用于执行自定义 SFTP 操作,比如查看 SFTP 默认目录(关于 ChannelSftp 的其他用法请参考 jsch 的 API):
```java
String dir = sftpTemplate.execute(ChannelSftp::pwd);
```
Jsch 的 ChannelSftp 提供了很多基础的方法,对于 execute 来说有点不太便捷,你可以使用 ChannelSftpWrapper 类来更便捷的使用 ChannelSftp,SftpTemplate 所有的方法也都是通过它来实现的。
### executeWithoutResult
`executeWithoutResult(SftpCallbackWithoutResult action)`用于执行自定义没有返回值的SFTP操作,比如下载文件(ChannelSftp的其他用途,请参考 jsch 的 API):
```java
try (OutputStream outputStream = Files.newOutputStream(Paths.get("/root/aptx4869.doc"))) {
sftpTemplate.executeWithoutResult(channelSftp -> channelSftp.get("aptx4869.doc", outputStream));
}
```
### 多 Host
在多 Host 使用 SftpTemplate 需要为 HaibaraCP 指定将要使用的连接,否则将抛出 `NullPointerException`,下面介绍了如何指定连接:
- `HostHolder.changeHost(string)` :通过 hostName (即指定配置文件 sftp.hosts 下 map 中的 key。后面的 hostName 不再重复说明) 指定下次使用的连接。注意它只能指定下一次的连接!!!
```java
HostHolder.changeHost("remote-1");
// 成功打印 remote-1 对应连接的原始目录
sftpTemplate.execute(ChannelSftp::pwd);
// 第二次执行失败,抛出空指针
sftpTemplate.execute(ChannelSftp::pwd);
```
- `HostHolder.changeHost(string, boolean)`:连续调用相同 host 连接时使用,避免执行一次 SftpTemplate 就要设置一次 hostName。注意要配合 `HostHolder.clearHost()` 使用!!!
```java
HostHolder.changeHost("remote-1", false);
try {
sftpTemplate.upload("D:\\aptx4869.docx", "/home/haibara/aptx4869.docx");
sftpTemplate.upload("D:\\aptx4869.pdf", "haibara/aptx4869.pdf");
sftpTemplate.upload("D:\\aptx4869.doc", "aptx4869.doc");
} finally {
HostHolder.clearHost();
}
```
- `HostHolder.hostNames()` 与 `HostHolder.hostNames(Predicate)`:获取所有或过滤后的 host 连接的 name。前面介绍的两种切换连接的方式都要显示指定 hostName,但有时需要批量执行配置的 n 个 host 连接,此时可以通过该方法获取所有或过滤后的 hostName 集合。
```java
// 获取所有以“remote-”开头的 hostName
for (String hostName : HostHolder.hostNames(s -> s.startsWith("remote-"))) {
HostHolder.changeHost(hostName);
sftpTemplate.upload("D:\\aptx4869.docx", "/home/haibara/aptx4869.docx");
}
```
## 密钥格式
OpenSSH 自 7.8 起,默认的密钥格式由
```
-----BEGIN RSA PRIVATE KEY-----
xxx
-----END RSA PRIVATE KEY-----
```
变更为:
```
-----BEGIN OPENSSH PRIVATE KEY-----
xxx
-----END OPENSSH PRIVATE KEY-----
```
Haibaracp 使用 Jsch 作为 SFTP 的实现,而 Jsch 不支持新的格式,因此你需要一些小改动:
1. 如果密钥由你生成,仅需在 `ssh-keygen` 命令后加上 `-m PEM` 以生成旧版的密钥继续使用。
2. 如果你无法自己获取旧版密钥,此时必须更改 POM,将 Jcraft 的 Jsch 更改为其他人 fork 的 Jsch 库(Jcraft 自 2018 年推送的 0.1.55 版本后没有任何的消息),比如:
```xml
io.github.hligaty
haibaracp-spring-boot-starter
1.2.3
com.jcraft
jsch
com.github.mwiede
jsch
0.1.72
```
否则你将看到 [JSchException: invalid privatekey](https://github.com/mwiede/jsch/issues/12#issuecomment-662863338)。
## 路线图
- [ ] 提供一个 SessionFactory Bean 用于自定义创建连接
- [ ] 移除对多个主机的原生支持
## Thanks for free JetBrains Open Source license
