java sftp demo

<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Vector;

import org.apache.log4j.Logger;
import org.springframework.util.StringUtils;

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 SFTPTool {
	/** 日志记录器 */
	private static Logger logger = Logger.getLogger(SFTPTool.class);
	/** Session */
	private static Session session = null;
	/** Channel */
	private static ChannelSftp channel = null;

	/** SFTP服务器IP地址 */
	private static String host;
	/** SFTP服务器端口 */
	private static Integer port;
	/** 连接超时时间,单位毫秒 */
	private static Integer timeout;
	/** 用户名 */
	private static String username;
	/** 密码 */
	private static String password;

	private static SFTPTool instance = null;

	public static synchronized SFTPTool getInstance() {
		if (instance == null) {
			instance = new SFTPTool();
			Properties prop = new Properties();
			// 配置文件
			InputStream in = null;
			try {
				in = SFTPTool.class.getClassLoader().getResourceAsStream("config.properties");
				prop.load(in);
				host = prop.getProperty("sftp.host");
				username = prop.getProperty("sftp.username");
				password = prop.getProperty("sftp.password");
				port = Integer.valueOf(prop.getProperty("sftp.port"));
				timeout = Integer.valueOf(prop.getProperty("sftp.timeout"));
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return instance;
	}

	private SFTPTool() {
	}

	/**
	* 登陆SFTP服务器
	* 
	* @return boolean
	*/
	public boolean login() {

		if (session != null && session.isConnected()) {
			return false;
		}

		if (channel != null && channel.isConnected()) {
			return false;
		}

		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.info("sftp session connected");

			logger.info("opening channel");
			channel = (ChannelSftp) session.openChannel("sftp");
			channel.connect();
			Field f;
			try {
				f = channel.getClass().getDeclaredField("server_version");
				f.setAccessible(true);
				f.set(channel, 2);
			} catch (Exception e) {
				e.printStackTrace();
			}
			try {
				channel.setFilenameEncoding("GBK");
			} catch (SftpException e) {
				e.printStackTrace();
			}

			logger.info("connected successfully");
			return true;
		} catch (JSchException e) {
			logger.error("sftp login failed", e);
			return false;
		}
	}

	/**
	* 上传文件
	* <p>
	* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
	* <table border="1">
	* <tr>
	* <td>当前目录</td>
	* <td>方法</td>
	* <td>参数:绝对路径/相对路径</td>
	* <td>上传后</td>
	* </tr>
	* <tr>
	* <td>/</td>
	* <td>uploadFile("testA","upload.txt",new FileInputStream(new
	* File("up.txt")))</td>
	* <td>相对路径</td>
	* <td>/testA/upload.txt</td>
	* </tr>
	* <tr>
	* <td>/</td>
	* <td>uploadFile("testA/testA_B","upload.txt",new FileInputStream(new
	* File("up.txt")))</td>
	* <td>相对路径</td>
	* <td>/testA/testA_B/upload.txt</td>
	* </tr>
	* <tr>
	* <td>/</td>
	* <td>uploadFile("/testA/testA_B","upload.txt",new FileInputStream(new
	* File("up.txt")))</td>
	* <td>绝对路径</td>
	* <td>/testA/testA_B/upload.txt</td>
	* </tr>
	* </table>
	* </p>
	* 
	* @param pathName
	*            SFTP服务器目录
	* @param fileName
	*            服务器上保存的文件名
	* @param input
	*            输入文件流
	* @return boolean
	*/
	public boolean uploadFile(String pathName, String fileName, InputStream input) {

		if (!existDir(pathName)) {
			mkdirs(pathName);
		}

		/*
		* String currentDir = currentDir(); if (!changeDir(pathName)) { return
		* false; }
		*/

		try {
			channel.put(input, fileName, ChannelSftp.OVERWRITE);
			if (!existFile(fileName)) {
				logger.info("upload failed");
				return false;
			}
			logger.info("upload successful");
			return true;
		} catch (SftpException e) {
			logger.error("upload failed", e);
			return false;
		} finally {
			// changeDir(currentDir);
		}
	}

	/**
	* 上传文件
	* <p>
	* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
	* <table border="1">
	* <tr>
	* <td>当前目录</td>
	* <td>方法</td>
	* <td>参数:绝对路径/相对路径</td>
	* <td>上传后</td>
	* </tr>
	* <tr>
	* <td>/</td>
	* <td>uploadFile("testA","upload.txt","up.txt")</td>
	* <td>相对路径</td>
	* <td>/testA/upload.txt</td>
	* </tr>
	* <tr>
	* <td>/</td>
	* <td>uploadFile("testA/testA_B","upload.txt","up.txt")</td>
	* <td>相对路径</td>
	* <td>/testA/testA_B/upload.txt</td>
	* </tr>
	* <tr>
	* <td>/</td>
	* <td>uploadFile("/testA/testA_B","upload.txt","up.txt")</td>
	* <td>绝对路径</td>
	* <td>/testA/testA_B/upload.txt</td>
	* </tr>
	* </table>
	* </p>
	* 
	* @param pathName
	*            SFTP服务器目录
	* @param fileName
	*            服务器上保存的文件名
	* @param localFile
	*            本地文件
	* @return boolean
	*/
	public boolean uploadFile(String pathName, String fileName, String localFile) {

		if (!existDir(pathName)) {
			mkdirs(pathName);
		}
		/*
		* String currentDir = currentDir(); if (!changeDir(pathName)) { return
		* false; }
		*/
		try {
			channel.put(localFile, fileName, ChannelSftp.OVERWRITE);
			if (!existFile(fileName)) {
				logger.info("upload failed");
				return false;
			}
			logger.info("upload successful");
			return true;
		} catch (SftpException e) {
			logger.error("upload failed", e);
			return false;
		} finally {
			// changeDir(currentDir);
		}
	}

	/**
	* 下载文件
	* <p>
	* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
	* <table border="1">
	* <tr>
	* <td>当前目录</td>
	* <td>方法</td>
	* <td>参数:绝对路径/相对路径</td>
	* <td>下载后</td>
	* </tr>
	* <tr>
	* <td>/</td>
	* <td>downloadFile("testA","down.txt","D:\\downDir")</td>
	* <td>相对路径</td>
	* <td>D:\\downDir\\down.txt</td>
	* </tr>
	* <tr>
	* <td>/</td>
	* <td>downloadFile("testA/testA_B","down.txt","D:\\downDir")</td>
	* <td>相对路径</td>
	* <td>D:\\downDir\\down.txt</td>
	* </tr>
	* <tr>
	* <td>/</td>
	* <td>downloadFile("/testA/testA_B","down.txt","D:\\downDir")</td>
	* <td>绝对路径</td>
	* <td>D:\\downDir\\down.txt</td>
	* </tr>
	* </table>
	* </p>
	* 
	* @param remotePath
	*            SFTP服务器目录
	* @param fileName
	*            服务器上需要下载的文件名
	* @param localPath
	*            本地保存路径
	* @return boolean
	*/
	public boolean downloadFile(String remotePath, String fileName, String localPath) {
		if (StringUtils.hasText(localPath)) {
			localPath.replaceAll("\\\\", "/");
		}
		currentDir();
		if (!changeDir(remotePath)) {

			return false;
		}

		try {
			localPath = localPath.endsWith("/") ? localPath.substring(0, localPath.length() - 1) : localPath;
			String localFilePath = localPath + "/" + fileName;
			channel.get(fileName, localFilePath);

			File localFile = new File(localFilePath);
			if (!localFile.exists()) {
				logger.info("download file failed");

				return false;
			}
			logger.info("download successful");

			return true;
		} catch (SftpException e) {
			logger.error("download file failed", e);

			return false;
		}
	}

	/**
	* 切换工作目录
	* <p>
	* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
	* <table border="1">
	* <tr>
	* <td>当前目录</td>
	* <td>方法</td>
	* <td>参数(绝对路径/相对路径)</td>
	* <td>切换后的目录</td>
	* </tr>
	* <tr>
	* <td>/</td>
	* <td>changeDir("testA")</td>
	* <td>相对路径</td>
	* <td>/testA/</td>
	* </tr>
	* <tr>
	* <td>/</td>
	* <td>changeDir("testA/testA_B")</td>
	* <td>相对路径</td>
	* <td>/testA/testA_B/</td>
	* </tr>
	* <tr>
	* <td>/</td>
	* <td>changeDir("/testA")</td>
	* <td>绝对路径</td>
	* <td>/testA/</td>
	* </tr>
	* <tr>
	* <td>/testA/testA_B/</td>
	* <td>changeDir("/testA")</td>
	* <td>绝对路径</td>
	* <td>/testA/</td>
	* </tr>
	* </table>
	* </p>
	* 
	* @param pathName
	*            路径
	* @return boolean
	*/
	public boolean changeDir(String pathName) {
		if (pathName == null || pathName.trim().equals("")) {
			logger.info("invalid pathName");
			return false;
		}

		try {
			channel.cd(pathName.replaceAll("\\\\", "/"));
			logger.info("directory successfully changed,current dir=" + channel.pwd());
			return true;
		} catch (SftpException e) {
			logger.error("failed to change directory", e);
			return false;
		}
	}

	/**
	* 切换到上一级目录
	* <p>
	* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
	* <table border="1">
	* <tr>
	* <td>当前目录</td>
	* <td>方法</td>
	* <td>切换后的目录</td>
	* </tr>
	* <tr>
	* <td>/testA/</td>
	* <td>changeToParentDir()</td>
	* <td>/</td>
	* </tr>
	* <tr>
	* <td>/testA/testA_B/</td>
	* <td>changeToParentDir()</td>
	* <td>/testA/</td>
	* </tr>
	* </table>
	* </p>
	* 
	* @return boolean
	*/
	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);
	}

	/**
	* 创建目录
	* <p>
	* 使用示例,SFTP服务器上的目录结构如下:/testA/testA_B/
	* <table border="1">
	* <tr>
	* <td>当前目录</td>
	* <td>方法</td>
	* <td>参数(绝对路径/相对路径)</td>
	* <td>创建成功后的目录</td>
	* </tr>
	* <tr>
	* <td>/testA/testA_B/</td>
	* <td>makeDir("testA_B_C")</td>
	* <td>相对路径</td>
	* <td>/testA/testA_B/testA_B_C/</td>
	* </tr>
	* <tr>
	* <td>/</td>
	* <td>makeDir("/testA/testA_B/testA_B_D")</td>
	* <td>绝对路径</td>
	* <td>/testA/testA_B/testA_B_D/</td>
	* </tr>
	* </table>
	* <br/>
	* <b>注意</b>,当<b>中间目录不存在</b>的情况下,不能够使用绝对路径的方式期望创建中间目录及目标目录。
	* 例如makeDir("/testNOEXIST1/testNOEXIST2/testNOEXIST3"),这是错误的。
	* </p>
	* 
	* @param dirName
	*            目录
	* @return boolean
	*/
	public boolean makeDir(String dirName) {
		try {
			channel.mkdir(dirName);
			logger.info("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")
	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.info("directory " + dirName + " successfully deleted");
			return true;
		} catch (SftpException e) {
			logger.error("failed to delete directory " + dirName, e);
			return false;
		}
	}

	/**
	* 删除文件
	* 
	* @param fileName
	*            文件名
	* @return boolean
	*/
	public boolean delFile(String fileName) {
		if (fileName == null || fileName.trim().equals("")) {
			logger.info("invalid filename");
			return false;
		}
		try {
			channel.rm(fileName);
			logger.info("file " + fileName + " successfully deleted");
			return true;
		} catch (SftpException e) {
			logger.error("failed to delete file " + fileName, e);
			return false;
		}
	}

	/**
	* 当前目录下文件及文件夹名称列表
	* 
	* @return String[]
	*/
	public String[] ls() {
		return list(Filter.ALL);
	}

	/**
	* 指定目录下文件及文件夹名称列表
	* 
	* @return String[]
	*/
	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 changeDir(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() {
		try {
			if (channel != null) {
				channel.quit();
				channel.disconnect();
			}
			if (session != null) {
				session.disconnect();
			}
			logger.info("logout successfully");
		} catch (Exception e) {
			logger.error(e);
		}
	}

	// ------private method ------

	/** 枚举,用于过滤文件和文件夹 */
	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;
	}

	/**
	* 根据字符串(地址路径)创建文件夹
	* 
	* @param names
	* @return
	* @throws UnsupportedEncodingException
	*/
	public boolean mkdirs(String str) {
		if (str == null || str.trim().equals("")) {
			return false;
		}
		String directory = str.endsWith("/") ? str : str + "/";
		try {
			if (!directory.equalsIgnoreCase("/") && !existDir(new String(directory.getBytes("GBK"), "iso-8859-1"))) {
				// 如果远程目录不存在,则递归创建远程服务器目录
				int start = 0;
				int end = 0;
				if (directory.startsWith("/")) {
					end = directory.indexOf("/", 1);
				} else {
					end = directory.indexOf("/", 0);
				}
				while (true) {
					String subDirectory = new String(str.substring(start, end).getBytes("GBK"), "iso-8859-1");
					if (!existDir(subDirectory)) {
						if (!makeDir(subDirectory)) {

							return false;
						}
					}
					changeDir(subDirectory);
					start = end + 1;
					end = directory.indexOf("/", start);
					// 检查所有目录是否创建完毕
					if (end <= start) {
						break;
					}
				}
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return false;
	}

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值