SFTP上传文件的小工具
临时需要将生成的数据上传到文件服务器,但是baidu出来的工具都有点局限,写个小池子方便后续使用
- 创建SFTP的连接池,主要用到一下几个主要依赖:jsch(java编写的实用SFTP包)、commons-pool(apache出品的一款连接池包)
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
- 创造一个对象获取yml或者properties文件中的属性:
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix="sftp")
@Data
public class SftpConfig {
private int ftpPort;
private String ftpHost;
private String ftpUserName;
private String password;
private String projectName;
private String charSet;
private int timeout;
private int maxTotal;
private int maxIdle;
private int minIdle;
private int maxWaitMillis;
}
- 创建连接对象工厂,便于生产连接对象
import com.jcraft.jsch.*;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.apache.commons.pool2.PooledObjectFactory;
import java.util.Properties;
/**
* @ClassName SftpFactory
* @Description
* @Author 80290644
* @Date 2020/11/2 19:54
*/
@Component
public class SftpFactory implements PooledObjectFactory<ChannelSftp> {
@Autowired
SftpConfig config;
//创建连接到池中
@Override
public PooledObject<ChannelSftp> makeObject() throws JSchException {
// 创建JSch
JSch jsch = new JSch();
// 根据用户名,主机ip,端口获取一个Session
Session session = jsch.getSession(config.getFtpUserName(), config.getFtpHost(), config.getFtpPort());
if (!StringUtils.isEmpty(config.getPassword())) {
// 设置密码
session.setPassword(config.getPassword());
}
Properties properties = new Properties();
properties.put("StrictHostKeyChecking", "no");
// 为Session对象设置properties
session.setConfig(properties);
// 设置timeout时间
session.setTimeout(config.getTimeout());
// 通过Session建立链接
session.connect(5000);
// 打开SFTP通道
Channel channel = session.openChannel("sftp");
//创建客户端实例
return new DefaultPooledObject<>((ChannelSftp) channel);
}
//销毁连接,当连接池空闲数量达到上限时,调用此方法销毁连接
@Override
public void destroyObject(PooledObject<ChannelSftp> pooledObject) {
ChannelSftp sftpObj = pooledObject.getObject();
if (sftpObj != null) {
try {
if (sftpObj.getSession() != null) {
sftpObj.getSession().disconnect();
if (sftpObj.isConnected()) {
sftpObj.exit();
}
}
} catch (JSchException e) {
e.printStackTrace();
}
if (sftpObj.isConnected()) {
sftpObj.exit();
}
}
}
//链接状态检查
@Override
public boolean validateObject(PooledObject<ChannelSftp> pooledObject) {
ChannelSftp sftpObj = pooledObject.getObject();
return sftpObj.isConnected();
}
//初始化连接
@Override
public void activateObject(PooledObject<ChannelSftp> pooledObject) throws Exception {
ChannelSftp sftpObject = pooledObject.getObject();
sftpObject.connect();
}
//钝化连接,使链接变为可用状态
@Override
public void passivateObject(PooledObject<ChannelSftp> pooledObject) throws Exception {
//FTPClient有此种钝化的方式,但是jsch不具备,不写了
}
//用于连接池中获取pool属性
public SftpConfig getConfig() {
return config;
}
}
- 再写一个池子(主角登场)
import com.jcraft.jsch.ChannelSftp;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* fileName:ftpPool
* description:FTP连接池
* 1.可以获取池中空闲链接
* 2.可以将链接归还到池中
* 3.当池中空闲链接不足时,可以创建链接
*
* @ClassName SftpPool
* @Description
* @Author 80290644
* @Date 2020/11/2 20:21
*/
@Component
public class SftpPool {
SftpFactory factory;
private final GenericObjectPool<ChannelSftp> internalPool;
//初始化连接池
public SftpPool(@Autowired SftpFactory factory) {
this.factory = factory;
SftpConfig config = factory.getConfig();
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(config.getMaxTotal());
poolConfig.setMinIdle(config.getMinIdle());
poolConfig.setMaxIdle(config.getMaxIdle());
poolConfig.setMaxWaitMillis(config.getMaxWaitMillis());
this.internalPool = new GenericObjectPool<>(factory, poolConfig);
}
//从连接池中取连接
public ChannelSftp getSftpClient() {
try {
return internalPool.borrowObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//将链接归还到连接池
public void returnSftpClient(ChannelSftp sftpObject) {
try {
internalPool.returnObject(sftpObject);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 销毁池子
*/
public void destroy() {
try {
internalPool.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 接下来利用这个池子封装方法就好了,写一个Util吧。因为只想上传,所以这只写了通过流上传的方法,其他下载等操作可以自定义类比实现。
/**
* 上传合并
* <pre>
* ----------命令集合---------------------
* 可参考链接(官方示例程序):http://www.jcraft.com/jsch/examples/Sftp.java
* ChannelSftp c = (ChannelSftp) channel;
* c.quit();
* c.exit();
* c.cd("/home/example");
* c.lcd("/home/example");
* c.rm("/home/example.gz");
* c.rmdir("/home/example");
* c.mkdir("/home/example");
* c.chgrp(777, "/home/example");
* c.chown(777, "/home/example");
* c.chmod(777, "/home/example");
* c.pwd();
* c.lpwd();
* c.ls("/home/example");
*
* SftpProgressMonitor monitor = new MyProgressMonitor(); //显示进度
* //文件下载
* c.get("srcPath", "dstPath", monitor, ChannelSftp.OVERWRITE);
* //断点续传
* c.get("srcPath", "dstPath", monitor, ChannelSftp.RESUME);
* //文件上传
* c.get("srcPath", "dstPath", monitor, ChannelSftp.APPEND);
* c.put("srcPath", "dstPath", monitor, ChannelSftp.APPEND);
* c.put("srcPath", "dstPath", monitor, ChannelSftp.APPEND);
* c.put("srcPath", "dstPath", monitor, ChannelSftp.APPEND);
*
* c.hardlink("oldPath", "newPath");
* c.rename("oldPath", "newPath");
* c.symlink("oldPath", "newPath");
* c.readlink("Path");
* c.realpath("Path");
* c.version();
*
* //df 命令
* SftpStatVFS stat = c.statVFS("path");
* long size = stat.getSize();
* long used = stat.getUsed();
* long avail = stat.getAvailForNonRoot();
* long root_avail = stat.getAvail();
* long capacity = stat.getCapacity();
*
* c.stat("path");
* c.lstat("path");
* ----------------------------------------------------------------------
* </pre>
*/
@Slf4j
@Component
public class SftpUtil {
@Resource
private SftpFactory sftpFactory;
@Resource
private SftpPool sftpPool;
public void uploadFileByFilePath(String src, String dst) {
try {
ChannelSftp channelSftp = sftpPool.getSftpClient();
channelSftp.put(src, dst, new SftpMonitor(), ChannelSftp.RESUME);
sftpPool.returnSftpClient(channelSftp);
} catch (SftpException e) {
e.printStackTrace();
}
}
public void uploadFileByResume(InputStream src, String dst) throws Exception {
ChannelSftp channelSftp = sftpPool.getSftpClient();
channelSftp.put(src, dst, new SftpMonitor(), ChannelSftp.OVERWRITE);
sftpPool.returnSftpClient(channelSftp);
}
public void uploadFileByAppend(InputStream src, String dst) throws Exception {
ChannelSftp channelSftp = sftpPool.getSftpClient();
channelSftp.put(src, dst, new SftpMonitor(), ChannelSftp.OVERWRITE);
sftpPool.returnSftpClient(channelSftp);
}
}