com.y3tu.tool.ssh.Sftp Maven / Gradle / Ivy
package com.y3tu.tool.ssh;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.ChannelSftp.LsEntrySelector;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import com.y3tu.tool.core.collection.ListUtil;
import com.y3tu.tool.core.reflect.Filter;
import com.y3tu.tool.core.text.StringUtils;
import com.y3tu.tool.ssh.ftp.FileProgressMonitor;
/**
* SFTP是Secure File Transfer Protocol的缩写,安全文件传送协议。可以为传输文件提供一种安全的加密方法。
* SFTP 为 SSH的一部份,是一种传输文件到服务器的安全方式。SFTP是使用加密传输认证信息和传输的数据,所以,使用SFTP是非常安全的。
* 但是,由于这种传输方式使用了加密/解密技术,所以传输效率比普通的FTP要低得多,如果您对网络安全性要求更高时,可以使用SFTP代替FTP。
*
*
* 此类为基于jsch的SFTP实现
* 参考:https://www.cnblogs.com/longyg/archive/2012/06/25/2556576.html
*
*
* @author looly
*
*/
public class Sftp implements Closeable {
private Session session;
private ChannelSftp channel;
// ---------------------------------------------------------------------------------------- Constructor start
/**
* 构造
*
* @param sshHost 远程主机
* @param sshPort 远程主机端口
* @param sshUser 远程主机用户名
* @param sshPass 远程主机密码
*/
public Sftp(String sshHost, int sshPort, String sshUser, String sshPass) {
this.session = JschUtil.getSession(sshHost, sshPort, sshUser, sshPass);
this.channel = JschUtil.openSftp(session);
}
/**
* 构造
*
* @param session {@link Session}
*/
public Sftp(Session session) {
this.session = session;
this.channel = JschUtil.openSftp(session);
}
/**
* 构造
*
* @param channel {@link ChannelSftp}
*/
public Sftp(ChannelSftp channel) {
this.channel = channel;
}
// ---------------------------------------------------------------------------------------- Constructor end
/**
* 获取SFTP通道
*
* @return 通道
*/
public ChannelSftp getChannel() {
return this.channel;
}
/**
* 远程当前目录
*
* @return 远程当前目录
*/
public String pwd() {
try {
return channel.pwd();
} catch (SftpException e) {
throw new JschRuntimeException(e);
}
}
/**
* 获取HOME路径
*
* @return HOME路径
* 4.0.5
*/
public String home() {
try {
return channel.getHome();
} catch (SftpException e) {
throw new JschRuntimeException(e);
}
}
/**
* 文件或目录在指定目录下是否存在
*
* @param path 目录
* @param fileOrDirName 文件或目录名
* @return 是否存在
* 4.0.5
*/
public boolean exist(String path, String fileOrDirName) {
List ls = ls(path);
return containsIgnoreCase(ls, fileOrDirName);
}
/**
* 遍历某个目录下所有文件或目录,不会递归遍历
*
* @param path 遍历某个目录下所有文件或目录
* @return 目录或文件名列表
*/
public List ls(String path) {
return ls(path, null);
}
/**
* 遍历某个目录下所有目录,不会递归遍历
*
* @param path 遍历某个目录下所有目录
* @return 目录名列表
*/
public List lsDirs(String path) {
return ls(path, new Filter() {
@Override
public boolean accept(LsEntry t) {
return t.getAttrs().isDir();
}
});
}
/**
* 遍历某个目录下所有文件,不会递归遍历
*
* @param path 遍历某个目录下所有文件
* @return 文件名列表
*/
public List lsFiles(String path) {
return ls(path, new Filter() {
@Override
public boolean accept(LsEntry t) {
return false == t.getAttrs().isDir();
}
});
}
/**
* 遍历某个目录下所有文件或目录,不会递归遍历
*
* @param path 遍历某个目录下所有文件或目录
* @param filter 文件或目录过滤器,可以实现过滤器返回自己需要的文件或目录名列表
* @return 目录或文件名列表
*/
public List ls(String path, final Filter filter) {
final List fileNames = new ArrayList<>();
try {
channel.ls(path, new LsEntrySelector() {
@Override
public int select(LsEntry entry) {
String fileName = entry.getFilename();
if (false == StringUtils.equals(".", fileName) && false == StringUtils.equals("..", fileName)) {
if (null == filter || filter.accept(entry)) {
fileNames.add(entry.getFilename());
}
}
return CONTINUE;
}
});
} catch (SftpException e) {
throw new JschRuntimeException(e);
}
return fileNames;
}
/**
* 打开上级目录
*
* @return 是否打开目录
*/
public boolean toParent() {
return cd("..");
}
/**
* 打开指定目录
*
* @param directory directory
* @return 是否打开目录
*/
public boolean cd(String directory) {
if (StringUtils.isBlank(directory)) {
return false;
}
try {
channel.cd(directory.replaceAll("\\\\", "/"));
return true;
} catch (SftpException e) {
return false;
}
}
/**
* 创建指定文件夹及其父目录
*
* @param dir 文件夹路径,绝对路径
*/
public void mkDirs(String dir) {
final String[] dirs = dir.split("[\\\\/]");
try {
final String now = channel.pwd();
channel.cd("/");
for (int i = 0; i < dirs.length; i++) {
if (StringUtils.isNotEmpty(dirs[i])) {
boolean dirExists = cd(dirs[i]);
if (false == dirExists) {
channel.mkdir(dirs[i]);
channel.cd(dirs[i]);
}
}
}
channel.cd(now);
} catch (SftpException e) {
throw new JschRuntimeException(e);
}
}
/**
* 删除文件
*
* @param filePath 要删除的文件绝对路径
*/
public Sftp delFile(String filePath) {
try {
channel.rm(filePath);
} catch (SftpException e) {
throw new JschRuntimeException(e);
}
return this;
}
/**
* 删除文件夹及其文件夹下的所有文件
*
* @param dirPath 文件夹路径
* @return boolean 是否删除成功
*/
@SuppressWarnings("unchecked")
public boolean delDir(String dirPath) {
if (false == cd(dirPath)) {
return false;
}
Vector list = null;
try {
list = channel.ls(channel.pwd());
} catch (SftpException e) {
throw new JschRuntimeException(e);
}
String fileName;
for (LsEntry entry : list) {
fileName = entry.getFilename();
if (false == fileName.equals(".") && false == fileName.equals("..")) {
if (entry.getAttrs().isDir()) {
delDir(fileName);
} else {
delFile(fileName);
}
}
}
if (false == cd("..")) {
return false;
}
// 删除空目录
try {
channel.rmdir(dirPath);
return true;
} catch (SftpException e) {
throw new JschRuntimeException(e);
}
}
/**
* 将本地文件上传到目标服务器,目标文件名为destPath,若destPath为目录,则目标文件名将与srcFilePath文件名相同。覆盖模式
*
* @param srcFilePath 本地文件路径
* @param destPath 目标路径,
* @return this
*/
public Sftp put(String srcFilePath, String destPath) {
return put(srcFilePath, destPath, Mode.OVERWRITE);
}
/**
* 将本地文件上传到目标服务器,目标文件名为destPath,若destPath为目录,则目标文件名将与srcFilePath文件名相同。
*
* @param srcFilePath 本地文件路径
* @param destPath 目标路径,
* @param mode {@link Mode} 模式
* @return this
*/
public Sftp put(String srcFilePath, String destPath, Mode mode) {
try {
channel.put(srcFilePath, destPath, new FileProgressMonitor(), mode.ordinal());
} catch (SftpException e) {
throw new JschRuntimeException(e);
}
return this;
}
/**
* 获取远程文件
*
* @param src 远程文件路径
* @param dest 目标文件路径
* @return this
*/
public Sftp get(String src, String dest) {
try {
channel.get(src, dest, new FileProgressMonitor());
} catch (SftpException e) {
throw new JschRuntimeException(e);
}
return this;
}
@Override
public void close() throws IOException {
JschUtil.close(this.channel);
JschUtil.close(this.session);
}
/**
* JSch支持的三种文件传输模式
*
* @author looly
*/
public static enum Mode {
/**
* 完全覆盖模式,这是JSch的默认文件传输模式,即如果目标文件已经存在,传输的文件将完全覆盖目标文件,产生新的文件。
*/
OVERWRITE,
/**
* 恢复模式,如果文件已经传输一部分,这时由于网络或其他任何原因导致文件传输中断,如果下一次传输相同的文件,则会从上一次中断的地方续传。
*/
RESUME,
/**
* 追加模式,如果目标文件已存在,传输的文件将在目标文件后追加。
*/
APPEND;
}
// ---------------------------------------------------------------------------------------------------------------------------------------- Private method start
/**
* 是否包含指定字符串,忽略大小写
*
* @param names 文件或目录名列表
* @param nameToFind 要查找的文件或目录名
* @return 是否包含
*/
private static boolean containsIgnoreCase(List names, String nameToFind) {
if (ListUtil.isEmpty(names)) {
return false;
}
if (StringUtils.isEmpty(nameToFind)) {
return false;
}
for (String name : names) {
if (nameToFind.equalsIgnoreCase(name)) {
return true;
}
}
return false;
}
// ---------------------------------------------------------------------------------------------------------------------------------------- Private method end
}