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);
}
}
