参看:https://www.cnblogs.com/longyg/archive/2012/06/25/2561332.html 《JSch - Java实现的SFTP(文件下载详解篇)》
http://www.cnblogs.com/longyg/archive/2012/06/25/2556576.html 《JSch - Java实现的SFTP(文件上传详解篇)》
http://www.idcfree.com/article-1428-1.html《Windows Server 下搭建sftp服务器, Freesshd安装及配置》
http://blog.csdn.net/ya2dan/article/details/19562605《SFTP连接异常 Could not connect to SFTP server.Caused by:com.jcraft.jsch.JSchException:Auth fail》
https://zhidao.baidu.com/question/509223259.html 《用java实现sftp下载,报2:No Such file》
SFTP是Secure File Transfer Protocol的缩写,安全文件传送协议。可以为传输文件提供一种安全的加密方法。SFTP 为 SSH的一部分,是一种传输文件到服务器的安全方式。SFTP是使用加密传输认证信息和传输的数据,所以,使用SFTP是非常安全的。但是,由于这种传输方式使用了加密/解密技术,所以传输效率比普通的FTP要低的多,如果您对网络安全性要求更高时,可以使用SFTP代替FTP。(来自百度的解释)
JSch 是 Java Secure Channel 的缩写。JSch是一个SSH2的纯Java实现。它允许你连接到一个SSH服务器,并且可以使用端口转发,X11转发,文件传输等,当然你也可以继承它的功能到你自己的应用程序。
要使用JSch, 需要下载它的jar包,请从官网下载它:http://www.jcraft.com/jsch/
一、ChannelSftp类
ChannelSftp类是JSch实现SFTP核心类,它包含了所有SFTP的方法,如:
put(): 文件上传
get(): 文件下载
cd(): 进入指定目录
ls(): 得到指定目录下的文件列表
rename(): 重命名指定文件或目录
rm(): 删除指定文件
mkdir(): 创建目录
rmdir(): 删除目录
等等;
JSch支持三种文件传输模式:
OVERWRITE:完全覆盖模式,这是JSch的默认文件传输模式,即如果目标文件已经存在,传输的文件将完全覆盖目标文件,产生新的文件。
RESUME : 恢复模式,如果文件已经传输一部分,这时由于网络或其他任何原因导致文件传输中断,如果下一次传输相同的文件,则会从上一次中断的地方续传。
APPEND:追加模式,如果目标文件已存在,传输的文件将在目标文件后追加。
二、实现SFTP操作的工具类
/*****************************************************************************************************
构造方法摘要--------------------------------------------------------------------------------------------
SftpChannel(String host, int port, int timeout, String username, String password)
参数:
host - SFTP服务器IP地址
port - SFTP服务器端口
timeout - 连接超时时间,单位毫秒
username - 用户名
password - 密码
方法摘要-----------------------------------------------------------------------------------------------
boolean changeDir(String pathName) : 切换工作目录
boolean changeToHomeDir() : 切换到根目录
boolean changeToParentDir() :切换到上一级目录
String currentDir() :当前工作目录
boolean delDir(String dirName) : 删除文件夹
boolean delFile(String fileName) :删除文件
boolean downloadFile(String remotePath, String fileName,String localPath) :下载文件
boolean exist(String name) : 当前目录是否存在文件或文件夹
boolean exist(String path, String name) :指定目录下,是否存在文件或文件夹
boolean existDir(String name) :当前目录是否存在文件夹
boolean existDir(String path, String name) : 指定目录下,是否存在文件夹
boolean existFile(String name) : 当前目录是否存在文件
boolean existFile(String path, String name) : 指定目录下,是否存在文件
boolean login() :登陆SFTP服务器
void logout() : 登出
String[] ls() :当前目录下文件及文件夹名称列表
String[] ls(String pathName) :指定目录下文件及文件夹名称列表
String[] lsDirs() : 当前目录下文件夹名称列表
String[] lsDirs(String pathName) :指定目录下文件夹名称列表
String[] lsFiles() :当前目录下文件名称列表
String[] lsFiles(String pathName) : 指定目录下文件名称列表
boolean makeDir(String dirName) :创建目录
boolean uploadFile(String pathName,String fileName,InputStream input) :上传文件
boolean uploadFile(String pathName, String fileName, String localFile) :上传文件
*****************************************************************************************************/
package com.myproject.util;
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;
public class SftpChannel {
private Logger logger = Logger.getLogger(SftpChannel.class);
private Session session = null;
private ChannelSftp channel = null;
private String host; // SFTP服务器IP地址
private int port; // SFTP服务器端口
private int timeout; // 连接超时时间,单位毫秒
private String username; // 用户名
private String password; // 密码
/**
* 构造器
* @param host SFTP服务器IP地址
* @param port SFTP服务器端口
* @param timeout 连接超时时间,单位毫秒
* @param username 用户名
* @param password 密码
*/
public SftpChannel(String host, int port, int timeout, String username, String password) {
this.host = host;
this.port = port;
this.timeout = timeout;
this.username = username;
this.password = password;
}
/**
* 登陆SFTP服务器
* @return 是否登录成功
*/
public boolean login() {
try {
JSch jsch = new JSch();
session = jsch.getSession(username, host, port);
if (password != null) {
session.setPassword(password);
}
Properties config = new Properties();
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;
}
}
/**
* 上传文件
* @param pathName SFTP服务器目录
* @param fileName 服务器上保存的文件名
* @param input 输入文件流
* @return
*/
public boolean 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);
}
}
/**
* 上传文件
* @param pathName SFTP服务器目录
* @param fileName 服务器上保存的文件名
* @param localFile 本地文件
* @return
*/
public boolean 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);
}
}
/**
* 下载文件
* @param remotePath SFTP服务器目录
* @param fileName 服务器上需要下载的文件名
* @param localPath 本地保存路径
* @return
*/
public boolean downloadFile(String remotePath, String fileName, String localPath) {
String currentDir = currentDir();
if (!changeDir(remotePath)) {
return false;
}
try {
String localFilePath = localPath + fileName;
channel.get(fileName, localFilePath);
File localFile = new File(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);
}
}
/**
* 切换工作目录
* @param pathName 路径
* @return
*/
public boolean 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;
}
}
/**
* 切换到上一级目录
* @return
*/
public boolean changeToParentDir() {
return changeDir("..");
}
/**
* 切换到根目录
* @return boolean
*/
public boolean changeToHomeDir() {
String homeDir = null;
try {
homeDir = channel.getHome();
} catch (SftpException e) {
logger.error("can not get home directory", e);
return false;
}
return changeDir(homeDir);
}
/**
* 创建目录
* @param dirName 目录
* @return
*/
public boolean 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
*/
@SuppressWarnings("unchecked")
public boolean delDir(String dirName) {
if (!changeDir(dirName)) {
return false;
}
Vector<LsEntry> 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
*/
public boolean 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
*/
public String[] ls() {
return list(Filter.ALL);
}
/**
* 指定目录下文件及文件夹名称列表
* @param pathName
* @return
*/
public String[] 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];
}
return result;
}
/**
* 当前目录下文件名称列表
* @return String[]
*/
public String[] lsFiles() {
return list(Filter.FILE);
}
/**
* 指定目录下文件名称列表
* @return String[]
*/
public String[] 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];
}
return result;
}
/**
* 当前目录下文件夹名称列表
* @return String[]
*/
public String[] lsDirs() {
return list(Filter.DIR);
}
/**
* 指定目录下文件夹名称列表
* @return String[]
*/
public String[] 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];
}
return result;
}
/**
* 当前目录是否存在文件或文件夹
* @param name 名称
* @return boolean
*/
public boolean exist(String name) {
return exist(ls(), name);
}
/**
* 指定目录下,是否存在文件或文件夹
* @param path 目录
* @param name 名称
* @return boolean
*/
public boolean exist(String path, String name) {
return exist(ls(path), name);
}
/**
* 当前目录是否存在文件
* @param name 文件名
* @return boolean
*/
public boolean existFile(String name) {
return exist(lsFiles(), name);
}
/**
* 指定目录下,是否存在文件
* @param path 目录
* @param name 文件名
* @return boolean
*/
public boolean existFile(String path, String name) {
return exist(lsFiles(path), name);
}
/**
* 当前目录是否存在文件夹
* @param name 文件夹名称
* @return boolean
*/
public boolean existDir(String name) {
return exist(lsDirs(), name);
}
/**
* 指定目录下,是否存在文件夹
* @param path 目录
* @param name 文家夹名称
* @return boolean
*/
public boolean existDir(String path, String name) {
return exist(lsDirs(path), name);
}
/**
* 当前工作目录
* @return String
*/
public String currentDir() {
try {
return channel.pwd();
} catch (SftpException e) {
logger.error("failed to get current dir", e);
return homeDir();
}
}
/**
* 登出
*/
public void logout() {
if (channel != null) {
channel.quit();
channel.disconnect();
}
if (session != null) {
session.disconnect();
}
logger.debug("logout successfully");
}
/** 枚举,用于过滤文件和文件夹 */
private enum Filter {
/** 文件及文件夹 */
ALL,
/** 文件 */
FILE,
/** 文件夹 */
DIR
};
/**
* 列出当前目录下的文件及文件夹
* @param filter 过滤参数
* @return String[]
*/
@SuppressWarnings("unchecked")
private String[] list(Filter filter) {
Vector<LsEntry> list = null;
try {
// ls方法会返回两个特殊的目录,当前目录(.)和父目录(..)
list = channel.ls(channel.pwd());
} catch (SftpException e) {
logger.error("can not list directory", e);
return new String[0];
}
List<String> resultList = new ArrayList<String>();
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
*/
private boolean 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
*/
private String homeDir() {
try {
return channel.getHome();
} catch (SftpException e) {
return "/";
}
}
/**
* 判断字符串是否存在于数组中
* @param strArr 字符串数组
* @param str 字符串
* @return boolean
*/
private boolean 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;
}
}