import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
import org.apache.log4j.Logger;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;/**
* SFTP(Secure File Transfer Protocol),安全文件传送协议。
*
* @version 1.0 2014/12/18
* @author dongliyang*/
public classSftp {/** 日志记录器*/
private Logger logger = Logger.getLogger(Sftp.class);/** Session*/
private Session session = null;/** Channel*/
private ChannelSftp channel = null;/** SFTP服务器IP地址*/
privateString host;/** SFTP服务器端口*/
private intport;/** 连接超时时间,单位毫秒*/
private inttimeout;/** 用户名*/
privateString username;/** 密码*/
privateString password;/**
* SFTP 安全文件传送协议
*
* @param host
* SFTP服务器IP地址
* @param port
* SFTP服务器端口
* @param timeout
* 连接超时时间,单位毫秒
* @param username
* 用户名
* @param password
* 密码*/
public Sftp(String host, int port, inttimeout, String username, String password) {this.host =host;this.port =port;this.timeout =timeout;this.username =username;this.password =password;
}/**
* 登陆SFTP服务器
*
* @return boolean*/
publicboolean login() {try{
JSch jsch= newJSch();
session=jsch.getSession(username, host, port);if (password != null) {
session.setPassword(password);
}
Properties config= newProperties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.setTimeout(timeout);
session.connect();
logger.debug("sftp session connected");
logger.debug("opening channel");
channel= (ChannelSftp) session.openChannel("sftp");
channel.connect();
logger.debug("connected successfully");return true;
}catch(JSchException e) {
logger.error("sftp login failed", e);return false;
}
}/**
* 上传文件
*
* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
*
*
*
当前目录*
方法*
参数:绝对路径/相对路径*
上传后*
*
*
/*
uploadFile("testA","upload.txt",new FileInputStream(new* File("up.txt")))
*
相对路径*
/testA/upload.txt*
*
*
/*
uploadFile("testA/testA_B","upload.txt",new FileInputStream(new* File("up.txt")))
*
相对路径*
/testA/testA_B/upload.txt*
*
*
/*
uploadFile("/testA/testA_B","upload.txt",new FileInputStream(new* File("up.txt")))
*
绝对路径*
/testA/testA_B/upload.txt*
*
*
*
* @param pathName
* SFTP服务器目录
* @param fileName
* 服务器上保存的文件名
* @param input
* 输入文件流
* @return boolean*/
publicboolean uploadFile(String pathName, String fileName, InputStream input) {
String currentDir=currentDir();if (!changeDir(pathName)) {return false;
}try{
channel.put(input, fileName, ChannelSftp.OVERWRITE);if (!existFile(fileName)) {
logger.debug("upload failed");return false;
}
logger.debug("upload successful");return true;
}catch(SftpException e) {
logger.error("upload failed", e);return false;
}finally{
changeDir(currentDir);
}
}/**
* 上传文件
*
* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
*
*
*
当前目录*
方法*
参数:绝对路径/相对路径*
上传后*
*
*
/*
uploadFile("testA","upload.txt","up.txt")*
相对路径*
/testA/upload.txt*
*
*
/*
uploadFile("testA/testA_B","upload.txt","up.txt")*
相对路径*
/testA/testA_B/upload.txt*
*
*
/*
uploadFile("/testA/testA_B","upload.txt","up.txt")*
绝对路径*
/testA/testA_B/upload.txt*
*
*
*
* @param pathName
* SFTP服务器目录
* @param fileName
* 服务器上保存的文件名
* @param localFile
* 本地文件
* @return boolean*/
publicboolean uploadFile(String pathName, String fileName, String localFile) {
String currentDir=currentDir();if (!changeDir(pathName)) {return false;
}try{
channel.put(localFile, fileName, ChannelSftp.OVERWRITE);if (!existFile(fileName)) {
logger.debug("upload failed");return false;
}
logger.debug("upload successful");return true;
}catch(SftpException e) {
logger.error("upload failed", e);return false;
}finally{
changeDir(currentDir);
}
}/**
* 下载文件
*
* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
*
*
*
当前目录*
方法*
参数:绝对路径/相对路径*
下载后*
*
*
/*
downloadFile("testA","down.txt","D:\\downDir")*
相对路径*
D:\\downDir\\down.txt*
*
*
/*
downloadFile("testA/testA_B","down.txt","D:\\downDir")*
相对路径*
D:\\downDir\\down.txt*
*
*
/*
downloadFile("/testA/testA_B","down.txt","D:\\downDir")*
绝对路径*
D:\\downDir\\down.txt*
*
*
*
* @param remotePath
* SFTP服务器目录
* @param fileName
* 服务器上需要下载的文件名
* @param localPath
* 本地保存路径
* @return boolean*/
publicboolean downloadFile(String remotePath, String fileName, String localPath) {
String currentDir=currentDir();if (!changeDir(remotePath)) {return false;
}try{
String localFilePath= localPath + File.separator +fileName;
channel.get(fileName, localFilePath);
File localFile= newFile(localFilePath);if (!localFile.exists()) {
logger.debug("download file failed");return false;
}
logger.debug("download successful");return true;
}catch(SftpException e) {
logger.error("download file failed", e);return false;
}finally{
changeDir(currentDir);
}
}/**
* 切换工作目录
*
* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
*
*
*
当前目录*
方法*
参数(绝对路径/相对路径)*
切换后的目录*
*
*
/*
changeDir("testA")*
相对路径*
/testA/*
*
*
/*
changeDir("testA/testA_B")*
相对路径*
/testA/testA_B/*
*
*
/*
changeDir("/testA")*
绝对路径*
/testA/*
*
*
/testA/testA_B/*
changeDir("/testA")*
绝对路径*
/testA/*
*
*
*
* @param pathName
* 路径
* @return boolean*/
publicboolean changeDir(String pathName) {if (pathName == null || pathName.trim().equals("")) {
logger.debug("invalid pathName");return false;
}try{
channel.cd(pathName.replaceAll("\\\\", "/"));
logger.debug("directory successfully changed,current dir=" +channel.pwd());return true;
}catch(SftpException e) {
logger.error("failed to change directory", e);return false;
}
}/**
* 切换到上一级目录
*
* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
*
*
*
当前目录*
方法*
切换后的目录*
*
*
/testA/*
changeToParentDir()*
/*
*
*
/testA/testA_B/*
changeToParentDir()*
/testA/*
*
*
*
* @return boolean*/
publicboolean changeToParentDir() {return changeDir("..");
}/**
* 切换到根目录
*
* @return boolean*/
publicboolean changeToHomeDir() {
String homeDir= null;try{
homeDir=channel.getHome();
}catch(SftpException e) {
logger.error("can not get home directory", e);return false;
}returnchangeDir(homeDir);
}/**
* 创建目录
*
* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
*
*
*
当前目录*
方法*
参数(绝对路径/相对路径)*
创建成功后的目录*
*
*
/testA/testA_B/*
makeDir("testA_B_C")*
相对路径*
/testA/testA_B/testA_B_C/*
*
*
/*
makeDir("/testA/testA_B/testA_B_D")*
绝对路径*
/testA/testA_B/testA_B_D/*
*
*
* 注意,当中间目录不存在的情况下,不能够使用绝对路径的方式期望创建中间目录及目标目录。
* 例如makeDir("/testNOEXIST1/testNOEXIST2/testNOEXIST3"),这是错误的。
*
*
* @param dirName
* 目录
* @return boolean*/
publicboolean makeDir(String dirName) {try{
channel.mkdir(dirName);
logger.debug("directory successfully created,dir=" +dirName);return true;
}catch(SftpException e) {
logger.error("failed to create directory", e);return false;
}
}/**
* 删除文件夹
*
* @param dirName
* @return boolean*/@SuppressWarnings("unchecked")publicboolean delDir(String dirName) {if (!changeDir(dirName)) {return false;
}
Vector list = null;try{
list=channel.ls(channel.pwd());
}catch(SftpException e) {
logger.error("can not list directory", e);return false;
}for(LsEntry entry : list) {
String fileName=entry.getFilename();if (!fileName.equals(".") && !fileName.equals("..")) {if(entry.getAttrs().isDir()) {
delDir(fileName);
}else{
delFile(fileName);
}
}
}if (!changeToParentDir()) {return false;
}try{
channel.rmdir(dirName);
logger.debug("directory" + dirName + "successfully deleted");return true;
}catch(SftpException e) {
logger.error("failed to delete directory" +dirName, e);return false;
}
}/**
* 删除文件
*
* @param fileName
* 文件名
* @return boolean*/
publicboolean delFile(String fileName) {if (fileName == null || fileName.trim().equals("")) {
logger.debug("invalid filename");return false;
}try{
channel.rm(fileName);
logger.debug("file" + fileName + "successfully deleted");return true;
}catch(SftpException e) {
logger.error("failed to delete file" +fileName, e);return false;
}
}/**
* 当前目录下文件及文件夹名称列表
*
* @return String[]*/
publicString[] ls() {returnlist(Filter.ALL);
}/**
* 指定目录下文件及文件夹名称列表
*
* @return String[]*/
publicString[] ls(String pathName) {
String currentDir=currentDir();if (!changeDir(pathName)) {return new String[0];
}
;
String[] result=list(Filter.ALL);if (!changeDir(currentDir)) {return new String[0];
}returnresult;
}/**
* 当前目录下文件名称列表
*
* @return String[]*/
publicString[] lsFiles() {returnlist(Filter.FILE);
}/**
* 指定目录下文件名称列表
*
* @return String[]*/
publicString[] lsFiles(String pathName) {
String currentDir=currentDir();if (!changeDir(pathName)) {return new String[0];
}
;
String[] result=list(Filter.FILE);if (!changeDir(currentDir)) {return new String[0];
}returnresult;
}/**
* 当前目录下文件夹名称列表
*
* @return String[]*/
publicString[] lsDirs() {returnlist(Filter.DIR);
}/**
* 指定目录下文件夹名称列表
*
* @return String[]*/
publicString[] lsDirs(String pathName) {
String currentDir=currentDir();if (!changeDir(pathName)) {return new String[0];
}
;
String[] result=list(Filter.DIR);if (!changeDir(currentDir)) {return new String[0];
}returnresult;
}/**
* 当前目录是否存在文件或文件夹
*
* @param name
* 名称
* @return boolean*/
publicboolean exist(String name) {returnexist(ls(), name);
}/**
* 指定目录下,是否存在文件或文件夹
*
* @param path
* 目录
* @param name
* 名称
* @return boolean*/
publicboolean exist(String path, String name) {returnexist(ls(path), name);
}/**
* 当前目录是否存在文件
*
* @param name
* 文件名
* @return boolean*/
publicboolean existFile(String name) {returnexist(lsFiles(), name);
}/**
* 指定目录下,是否存在文件
*
* @param path
* 目录
* @param name
* 文件名
* @return boolean*/
publicboolean existFile(String path, String name) {returnexist(lsFiles(path), name);
}/**
* 当前目录是否存在文件夹
*
* @param name
* 文件夹名称
* @return boolean*/
publicboolean existDir(String name) {returnexist(lsDirs(), name);
}/**
* 指定目录下,是否存在文件夹
*
* @param path
* 目录
* @param name
* 文家夹名称
* @return boolean*/
publicboolean existDir(String path, String name) {returnexist(lsDirs(path), name);
}/**
* 当前工作目录
*
* @return String*/
publicString currentDir() {try{returnchannel.pwd();
}catch(SftpException e) {
logger.error("failed to get current dir", e);returnhomeDir();
}
}/**
* 登出*/
public voidlogout() {if (channel != null) {
channel.quit();
channel.disconnect();
}if (session != null) {
session.disconnect();
}
logger.debug("logout successfully");
}//------private method ------
/** 枚举,用于过滤文件和文件夹*/
private enumFilter {/** 文件及文件夹*/ALL,/** 文件*/FILE,/** 文件夹*/DIR
};/**
* 列出当前目录下的文件及文件夹
*
* @param filter
* 过滤参数
* @return String[]*/@SuppressWarnings("unchecked")privateString[] list(Filter filter) {
Vector list = null;try{//ls方法会返回两个特殊的目录,当前目录(.)和父目录(..)
list =channel.ls(channel.pwd());
}catch(SftpException e) {
logger.error("can not list directory", e);return new String[0];
}
List resultList = new ArrayList();for(LsEntry entry : list) {if(filter(entry, filter)) {
resultList.add(entry.getFilename());
}
}return resultList.toArray(new String[0]);
}/**
* 判断是否是否过滤条件
*
* @param entry
* LsEntry
* @param f
* 过滤参数
* @return boolean*/
privateboolean filter(LsEntry entry, Filter f) {if(f.equals(Filter.ALL)) {return !entry.getFilename().equals(".") && !entry.getFilename().equals("..");
}else if(f.equals(Filter.FILE)) {return !entry.getFilename().equals(".") && !entry.getFilename().equals("..") && !entry.getAttrs().isDir();
}else if(f.equals(Filter.DIR)) {return !entry.getFilename().equals(".") && !entry.getFilename().equals("..") &&entry.getAttrs().isDir();
}return false;
}/**
* 根目录
*
* @return String*/
privateString homeDir() {try{returnchannel.getHome();
}catch(SftpException e) {return "/";
}
}/**
* 判断字符串是否存在于数组中
*
* @param strArr
* 字符串数组
* @param str
* 字符串
* @return boolean*/
privateboolean exist(String[] strArr, String str) {if (strArr == null || strArr.length == 0) {return false;
}if (str == null || str.trim().equals("")) {return false;
}for(String s : strArr) {if(s.equalsIgnoreCase(str)) {return true;
}
}return false;
}//------private method ------
}