JSch是Java Secure Channel的縮寫。JSch是一個SSH2的純Java實現。它允許你連接到一個SSH服務器,並且可以使用端口轉發,X11轉發,文件傳輸等,當然你也可以集成它的功能到你自己的應用程序。
本文只介紹如何使用JSch實現的SFTP功能。
SFTP是Secure File Transfer Protocol的縮寫,安全文件傳送協議。可以為傳輸文件提供一種安全的加密方法。SFTP 為 SSH的一部份,是一種傳輸文件到服務器的安全方式。SFTP是使用加密傳輸認證信息和傳輸的數據,所以,使用SFTP是非常安全的。但是,由於這種傳輸方式使用了加密/解密技術,所以傳輸效率比普通的FTP要低得多,如果您對網絡安全性要求更高時,可以使用SFTP代替FTP。(來自百度的解釋)
要使用JSch,需要下載它的jar包,請從官網下載它:
http://www.jcraft.com/jsch/
ChannelSftp類是JSch實現SFTP核心類,它包含了所有SFTP的方法,如:
put(): 文件上傳
get(): 文件下載
cd(): 進入指定目錄
ls(): 得到指定目錄下的文件列表
rename(): 重命名指定文件或目錄
rm(): 刪除指定文件
mkdir(): 創建目錄
rmdir(): 刪除目錄
等等(這里省略了方法的參數,put和get都有多個重載方法,具體請看源代碼,這里不一一列出。)

步驟:
- 根據Jsch創建Session;
- 設置Session密碼、超時時間和屬性等;
- 連接session;
- 使用Session創建ChannelSftp通道;
- 接下來就可以使用ChannelSftp進行各種操作了:如文件上傳、文件下載;
- 最后,關系各種資源:如Session、ChannelSftp等;
其他:還可以設置監聽器,監控文件上傳和下載的進度;
創建ChannelSftp對象
編寫一個工具類,根據ip,用戶名及密碼得到一個SFTP channel對象,即ChannelSftp的實例對象,在應用程序中就可以使用該對象來調用SFTP的各種操作方法。



監控傳輸進度

文件上傳



測試斷點續傳






完整程序
package com.sssppp.Communication;
/**
* This program will demonstrate the sftp protocol support.
* $ CLASSPATH=.:../build javac Sftp.java
* $ CLASSPATH=.:../build java Sftp
* You will be asked username, host and passwd.
* If everything works fine, you will get a prompt 'sftp>'.
* 'help' command will show available command.
* In current implementation, the destination path for 'get' and 'put'
* commands must be a file, not a directory.
*
*/
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.ProgressMonitor;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpProgressMonitor;
/**
* <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();
*
* SftpStatVFS stat = c.statVFS("path"); //df 命令
* 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>
*
*/
public class SftpUtil {
Session session = null;
Channel channel = null;
public static final String SFTP_REQ_HOST = "host";
public static final String SFTP_REQ_PORT = "port";
public static final String SFTP_REQ_USERNAME = "username";
public static final String SFTP_REQ_PASSWORD = "password";
public static final int SFTP_DEFAULT_PORT = 22;
public static final String SFTP_REQ_LOC = "location";
/**
* 測試程序
* @param arg
* @throws Exception
*/
public static void main(String[] arg) throws Exception {
// 設置主機ip,端口,用戶名,密碼
Map<String, String> sftpDetails = new HashMap<String, String>();
sftpDetails.put(SFTP_REQ_HOST, "10.180.137.221");
sftpDetails.put(SFTP_REQ_USERNAME, "root");
sftpDetails.put(SFTP_REQ_PASSWORD, "xxx");
sftpDetails.put(SFTP_REQ_PORT, "22");
//測試文件上傳
String src = "C:\\xxx\\TMP\\site-1.10.4.zip"; // 本地文件名
String dst = "/tmp/sftp/"; // 目標文件名
uploadFile(src, dst, sftpDetails);
}
public static void uploadFile(String src, String dst,
Map<String, String> sftpDetails) throws Exception {
SftpUtil sftpUtil = new SftpUtil();
ChannelSftp chSftp = sftpUtil.getChannel(sftpDetails, 60000);
/**
* 代碼段1/代碼段2/代碼段3分別演示了如何使用JSch的不同的put方法來進行文件上傳。這三段代碼實現的功能是一樣的,
* 都是將本地的文件src上傳到了服務器的dst文件
*/
/**代碼段1
OutputStream out = chSftp.put(dst,new MyProgressMonitor2(), ChannelSftp.OVERWRITE); // 使用OVERWRITE模式
byte[] buff = new byte[1024 * 256]; // 設定每次傳輸的數據塊大小為256KB
int read;
if (out != null) {
InputStream is = new FileInputStream(src);
do {
read = is.read(buff, 0, buff.length);
if (read > 0) {
out.write(buff, 0, read);
}
out.flush();
} while (read >= 0);
}
**/
// 使用這個方法時,dst可以是目錄,當dst是目錄時,上傳后的目標文件名將與src文件名相同
// ChannelSftp.RESUME:斷點續傳
chSftp.put(src, dst, new MyProgressMonitor(), ChannelSftp.RESUME); // 代碼段2
// 將本地文件名為src的文件輸入流上傳到目標服務器,目標文件名為dst。
// chSftp.put(new FileInputStream(src), dst,new MyProgressMonitor2(), ChannelSftp.OVERWRITE); // 代碼段3
chSftp.quit();
sftpUtil.closeChannel();
}
/**
* 根據ip,用戶名及密碼得到一個SFTP
* channel對象,即ChannelSftp的實例對象,在應用程序中就可以使用該對象來調用SFTP的各種操作方法
*
* @param sftpDetails
* @param timeout
* @return
* @throws JSchException
*/
public ChannelSftp getChannel(Map<String, String> sftpDetails, int timeout)
throws JSchException {
String ftpHost = sftpDetails.get(SFTP_REQ_HOST);
String port = sftpDetails.get(SFTP_REQ_PORT);
String ftpUserName = sftpDetails.get(SFTP_REQ_USERNAME);
String ftpPassword = sftpDetails.get(SFTP_REQ_PASSWORD);
int ftpPort = SFTP_DEFAULT_PORT;
if (port != null && !port.equals("")) {
ftpPort = Integer.valueOf(port);
}
JSch jsch = new JSch(); // 創建JSch對象
session = jsch.getSession(ftpUserName, ftpHost, ftpPort); // 根據用戶名,主機ip,端口獲取一個Session對象
if (ftpPassword != null) {
session.setPassword(ftpPassword); // 設置密碼
}
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config); // 為Session對象設置properties
session.setTimeout(timeout); // 設置timeout時間
session.connect(5000); // 通過Session建立鏈接
channel = session.openChannel("sftp"); // 打開SFTP通道
channel.connect(); // 建立SFTP通道的連接
return (ChannelSftp) channel;
}
public void closeChannel() throws Exception {
if (channel != null) {
channel.disconnect();
}
if (session != null) {
session.disconnect();
}
}
/**
* 進度監控器-JSch每次傳輸一個數據塊,就會調用count方法來實現主動進度通知
*
*/
public static class MyProgressMonitor implements SftpProgressMonitor {
private long count = 0; //當前接收的總字節數
private long max = 0; //最終文件大小
private long percent = -1; //進度
/**
* 當每次傳輸了一個數據塊后,調用count方法,count方法的參數為這一次傳輸的數據塊大小
*/
@Override
public boolean count(long count) {
this.count += count;
if (percent >= this.count * 100 / max) {
return true;
}
percent = this.count * 100 / max;
System.out.println("Completed " + this.count + "(" + percent
+ "%) out of " + max + ".");
return true;
}
/**
* 當傳輸結束時,調用end方法
*/
@Override
public void end() {
System.out.println("Transferring done.");
}
/**
* 當文件開始傳輸時,調用init方法
*/
@Override
public void init(int op, String src, String dest, long max) {
System.out.println("Transferring begin.");
this.max = max;
this.count = 0;
this.percent = -1;
}
}
/**
* 官方提供的進度監控器
*
*/
public static class DemoProgressMonitor implements SftpProgressMonitor {
ProgressMonitor monitor;
long count = 0;
long max = 0;
/**
* 當文件開始傳輸時,調用init方法。
*/
public void init(int op, String src, String dest, long max) {
this.max = max;
monitor = new ProgressMonitor(null,
((op == SftpProgressMonitor.PUT) ? "put" : "get") + ": "
+ src, "", 0, (int) max);
count = 0;
percent = -1;
monitor.setProgress((int) this.count);
monitor.setMillisToDecideToPopup(1000);
}
private long percent = -1;
/**
* 當每次傳輸了一個數據塊后,調用count方法,count方法的參數為這一次傳輸的數據塊大小。
*/
public boolean count(long count) {
this.count += count;
if (percent >= this.count * 100 / max) {
return true;
}
percent = this.count * 100 / max;
monitor.setNote("Completed " + this.count + "(" + percent
+ "%) out of " + max + ".");
monitor.setProgress((int) this.count);
return !(monitor.isCanceled());
}
/**
* 當傳輸結束時,調用end方法。
*/
public void end() {
monitor.close();
}
}
}