JSch是Java Secure Channel的縮寫。JSch是一個SSH2的純Java實現。它容許你鏈接到一個SSH服務器,而且可使用端口轉發,X11轉發,文件傳輸等,固然你也能夠集成它的功能到你本身的應用程序。html
本文只介紹如何使用JSch實現的SFTP功能。java
SFTP是Secure File Transfer Protocol的縮寫,安全文件傳送協議。能夠爲傳輸文件提供一種安全的加密方法。SFTP 爲 SSH的一部份,是一種傳輸文件到服務器的安全方式。SFTP是使用加密傳輸認證信息和傳輸的數據,因此,使用SFTP是很是安全的。可是,因爲這種傳輸方式使用了加密/解密技術,因此傳輸效率比普通的FTP要低得多,若是您對網絡安全性要求更高時,可使用SFTP代替FTP。(來自百度的解釋) apache
要使用JSch,須要下載它的jar包,請從官網下載它:http://www.jcraft.com/jsch/安全
ChannelSftp類是JSch實現SFTP核心類,它包含了全部SFTP的方法,如:服務器
put(): 文件上傳網絡
get(): 文件下載session
cd(): 進入指定目錄ide
ls(): 獲得指定目錄下的文件列表函數
rename(): 重命名指定文件或目錄工具
rm(): 刪除指定文件
mkdir(): 建立目錄
rmdir(): 刪除目錄
等等(這裏省略了方法的參數,put和get都有多個重載方法,具體請看源代碼,這裏不一一列出。)
JSch支持三種文件傳輸模式:
OVERWRITE | 徹底覆蓋模式,這是JSch的默認文件傳輸模式,即若是目標文件已經存在,傳輸的文件將徹底覆蓋目標文件,產生新的文件。 |
RESUME | 恢復模式,若是文件已經傳輸一部分,這時因爲網絡或其餘任何緣由致使文件傳輸中斷,若是下一次傳輸相同的文件, 則會從上一次中斷的地方續傳。 |
APPEND | 追加模式,若是目標文件已存在,傳輸的文件將在目標文件後追加。 |
建立ChannelSftp對象
編寫一個工具類,根據ip,用戶名及密碼獲得一個SFTP channel對象,即ChannelSftp的實例對象,在應用程序中就可使用該對象來調用SFTP的各類操做方法。
1 package com.longyg.sftp; 2 3 import java.util.Map; 4 import java.util.Properties; 5 import org.apache.log4j.Logger; 6 import com.jcraft.jsch.Channel; 7 import com.jcraft.jsch.ChannelSftp; 8 import com.jcraft.jsch.JSch; 9 import com.jcraft.jsch.JSchException; 10 import com.jcraft.jsch.Session; 11 public class SFTPChannel { 12 Session session = null; 13 Channel channel = null; 14 private static final Logger LOG = Logger.getLogger(SFTPChannel.class.getName()); 15 public ChannelSftp getChannel(Map<String, String> sftpDetails, int timeout) throws JSchException { 16 String ftpHost = sftpDetails.get(SFTPConstants.SFTP_REQ_HOST); 17 String port = sftpDetails.get(SFTPConstants.SFTP_REQ_PORT); 18 String ftpUserName = sftpDetails.get(SFTPConstants.SFTP_REQ_USERNAME); 19 String ftpPassword = sftpDetails.get(SFTPConstants.SFTP_REQ_PASSWORD); 20 int ftpPort = SFTPConstants.SFTP_DEFAULT_PORT; 21 if (port != null && !port.equals("")) { 22 ftpPort = Integer.valueOf(port); 23 } 24 JSch jsch = new JSch(); // 建立JSch對象 25 session = jsch.getSession(ftpUserName, ftpHost, ftpPort); // 根據用戶名,主機ip,端口獲取一個Session對象 26 LOG.debug("Session created."); 27 if (ftpPassword != null) { 28 session.setPassword(ftpPassword); // 設置密碼 29 } 30 Properties config = new Properties(); 31 config.put("StrictHostKeyChecking", "no"); 32 session.setConfig(config); // 爲Session對象設置properties 33 session.setTimeout(timeout); // 設置timeout時間 34 session.connect(); // 經過Session創建連接 35 LOG.debug("Session connected."); 36 LOG.debug("Opening Channel."); 37 channel = session.openChannel("sftp"); // 打開SFTP通道 38 channel.connect(); // 創建SFTP通道的鏈接 39 LOG.debug("Connected successfully to ftpHost = " + ftpHost + ",as ftpUserName = " + ftpUserName 40 + ", returning: " + channel); 41 return (ChannelSftp) channel; 42 } 43 public void closeChannel() throws Exception { 44 if (channel != null) { 45 channel.disconnect(); 46 } 47 if (session != null) { 48 session.disconnect(); 49 } 50 } 51 }
SFTPConstants是一個靜態成員變量類:
1 package com.longyg.sftp; 2 3 public class SFTPConstants { 4 public static final String SFTP_REQ_HOST = "host"; 5 public static final String SFTP_REQ_PORT = "port"; 6 public static final String SFTP_REQ_USERNAME = "username"; 7 public static final String SFTP_REQ_PASSWORD = "password"; 8 public static final int SFTP_DEFAULT_PORT = 22; 9 public static final String SFTP_REQ_LOC = "location"; 10 }
文件上傳
實現文件上傳能夠調用ChannelSftp對象的put方法。ChannelSftp中有12個put方法的重載方法:
public void put(String src, String dst) | 將本地文件名爲src的文件上傳到目標服務器,目標文件名爲dst,若dst爲目錄,則目標文件名將與src文件名相同。 採用默認的傳輸模式:OVERWRITE |
public void put(String src, String dst, int mode) | 將本地文件名爲src的文件上傳到目標服務器,目標文件名爲dst,若dst爲目錄,則目標文件名將與src文件名相同。 指定文件傳輸模式爲mode(mode可選值爲:ChannelSftp.OVERWRITE,ChannelSftp.RESUME, ChannelSftp.APPEND) |
public void put(String src, String dst, SftpProgressMonitor monitor) |
將本地文件名爲src的文件上傳到目標服務器,目標文件名爲dst,若dst爲目錄,則目標文件名將與src文件名相同。 採用默認的傳輸模式:OVERWRITE 並使用實現了SftpProgressMonitor接口的monitor對象來監控文件傳輸的進度。 |
public void put(String src, String dst, |
將本地文件名爲src的文件上傳到目標服務器,目標文件名爲dst,若dst爲目錄,則目標文件名將與src文件名相同。 指定傳輸模式爲mode 並使用實現了SftpProgressMonitor接口的monitor對象來監控文件傳輸的進度。 |
public void put(InputStream src, String dst) | 將本地的input stream對象src上傳到目標服務器,目標文件名爲dst,dst不能爲目錄。 採用默認的傳輸模式:OVERWRITE |
public void put(InputStream src, String dst, int mode) | 將本地的input stream對象src上傳到目標服務器,目標文件名爲dst,dst不能爲目錄。 指定文件傳輸模式爲mode |
public void put(InputStream src, String dst, SftpProgressMonitor monitor) |
將本地的input stream對象src上傳到目標服務器,目標文件名爲dst,dst不能爲目錄。 採用默認的傳輸模式:OVERWRITE 並使用實現了SftpProgressMonitor接口的monitor對象來監控傳輸的進度。 |
public void put(InputStream src, String dst, |
將本地的input stream對象src上傳到目標服務器,目標文件名爲dst,dst不能爲目錄。 指定文件傳輸模式爲mode 並使用實現了SftpProgressMonitor接口的monitor對象來監控傳輸的進度。 |
public OutputStream put(String dst) | 該方法返回一個輸出流,能夠向該輸出流中寫入數據,最終將數據傳輸到目標服務器,目標文件名爲dst,dst不能爲目錄。 採用默認的傳輸模式:OVERWRITE |
public OutputStream put(String dst, final int mode) | 該方法返回一個輸出流,能夠向該輸出流中寫入數據,最終將數據傳輸到目標服務器,目標文件名爲dst,dst不能爲目錄。 指定文件傳輸模式爲mode |
public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode) | 該方法返回一個輸出流,能夠向該輸出流中寫入數據,最終將數據傳輸到目標服務器,目標文件名爲dst,dst不能爲目錄。 指定文件傳輸模式爲mode 並使用實現了SftpProgressMonitor接口的monitor對象來監控傳輸的進度。 |
public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode, long offset) | 該方法返回一個輸出流,能夠向該輸出流中寫入數據,最終將數據傳輸到目標服務器,目標文件名爲dst,dst不能爲目錄。 指定文件傳輸模式爲mode 並使用實現了SftpProgressMonitor接口的monitor對象來監控傳輸的進度。 offset指定了一個偏移量,從輸出流偏移offset開始寫入數據。 |
應用實例:
1 package com.longyg.sftp; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 import com.jcraft.jsch.ChannelSftp; 6 public class SFTPTest { 7 public SFTPChannel getSFTPChannel() { 8 return new SFTPChannel(); 9 } 10 /** 11 * @param args 12 * @throws Exception 13 */ 14 public static void main(String[] args) throws Exception { 15 SFTPTest test = new SFTPTest(); 16 Map<String, String> sftpDetails = new HashMap<String, String>(); 17 // 設置主機ip,端口,用戶名,密碼 18 sftpDetails.put(SFTPConstants.SFTP_REQ_HOST, "10.9.167.55"); 19 sftpDetails.put(SFTPConstants.SFTP_REQ_USERNAME, "root"); 20 sftpDetails.put(SFTPConstants.SFTP_REQ_PASSWORD, "arthur"); 21 sftpDetails.put(SFTPConstants.SFTP_REQ_PORT, "22"); 22 23 String src = "D:\\DevSoft\\HB-SnagIt1001.rar"; // 本地文件名 24 String dst = "/home/omc/ylong/sftp/HB-SnagIt1001.rar"; // 目標文件名 25 26 SFTPChannel channel = test.getSFTPChannel(); 27 ChannelSftp chSftp = channel.getChannel(sftpDetails, 60000); 28 29 /** 30 * 代碼段1 31 OutputStream out = chSftp.put(dst, ChannelSftp.OVERWRITE); // 使用OVERWRITE模式 32 byte[] buff = new byte[1024 * 256]; // 設定每次傳輸的數據塊大小爲256KB 33 int read; 34 if (out != null) { 35 System.out.println("Start to read input stream"); 36 InputStream is = new FileInputStream(src); 37 do { 38 read = is.read(buff, 0, buff.length); 39 if (read > 0) { 40 out.write(buff, 0, read); 41 } 42 out.flush(); 43 } while (read >= 0); 44 System.out.println("input stream read done."); 45 } 46 **/ 47 48 chSftp.put(src, dst, ChannelSftp.OVERWRITE); // 代碼段2 49 50 // chSftp.put(new FileInputStream(src), dst, ChannelSftp.OVERWRITE); // 代碼段3 51 52 chSftp.quit(); 53 channel.closeChannel(); 54 } 55 }
注:請分別將代碼段1,代碼段2,代碼段3取消註釋,運行程序來進行測試。這三段代碼分別演示瞭如何使用JSch的不一樣的put方法來進行文件上傳。
代碼段1:採用向put方法返回的輸出流中寫入數據的方式來傳輸文件。 須要由程序來決定寫入什麼樣的數據,這裏是將本地文件的輸入流寫入輸出流。採用這種方式的好處是,能夠自行設定每次寫入輸出流的數據塊大小,如本示例中的語句:
byte[] buff = new byte[1024 * 256]; // 設定每次傳輸的數據塊大小爲256KB
代碼段2:直接將本地文件名爲src的文件上傳到目標服務器,目標文件名爲dst。(注:使用這個方法時,dst能夠是目錄,當dst是目錄時,上傳後的目標文件名將與src文件名相同)
代碼段3:將本地文件名爲src的文件輸入流上傳到目標服務器,目標文件名爲dst。
這三段代碼實現的功能是同樣的,都是將本地的文件src上傳到了服務器的dst文件。使用時可根據具體狀況選擇使用哪一種實現方式。
監控傳輸進度
從前面的介紹中知道,JSch支持在文件傳輸時對傳輸進度的監控。能夠實現JSch提供的SftpProgressMonitor接口來完成這個功能。
SftpProgressMonitor接口類的定義爲:
1 package com.jcraft.jsch; 2 3 public interface SftpProgressMonitor{ 4 public static final int PUT=0; 5 public static final int GET=1; 6 void init(int op, String src, String dest, long max); 7 boolean count(long count); 8 void end(); 9 }
init(): 當文件開始傳輸時,調用init方法。
count(): 當每次傳輸了一個數據塊後,調用count方法,count方法的參數爲這一次傳輸的數據塊大小。
end(): 當傳輸結束時,調用end方法。
下面是一個簡單的實現:
1 package com.longyg.sftp; 2 3 import com.jcraft.jsch.SftpProgressMonitor; 4 5 public class MyProgressMonitor implements SftpProgressMonitor { 6 private long transfered; 7 @Override 8 public boolean count(long count) { 9 transfered = transfered + count; 10 System.out.println("Currently transferred total size: " + transfered + " bytes"); 11 return true; 12 } 13 @Override 14 public void end() { 15 System.out.println("Transferring done."); 16 } 17 @Override 18 public void init(int op, String src, String dest, long max) { 19 System.out.println("Transferring begin."); 20 } 21 }
此時若是改變SFTPTest main方法裏調用的put方法,便可實現監控傳輸進度:
1 package com.longyg.sftp; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 import com.jcraft.jsch.ChannelSftp; 6 7 public class SFTPTest { 8 public SFTPChannel getSFTPChannel() { 9 return new SFTPChannel(); 10 } 11 /** 12 * @param args 13 * @throws Exception 14 */ 15 public static void main(String[] args) throws Exception { 16 SFTPTest test = new SFTPTest(); 17 Map<String, String> sftpDetails = new HashMap<String, String>(); 18 // 設置主機ip,端口,用戶名,密碼 19 sftpDetails.put(SFTPConstants.SFTP_REQ_HOST, "10.9.167.55"); 20 sftpDetails.put(SFTPConstants.SFTP_REQ_USERNAME, "root"); 21 sftpDetails.put(SFTPConstants.SFTP_REQ_PASSWORD, "arthur"); 22 sftpDetails.put(SFTPConstants.SFTP_REQ_PORT, "22"); 23 24 String src = "D:\\DevSoft\\HB-SnagIt1001.rar"; // 本地文件名 25 String dst = "/home/omc/ylong/sftp/HB-SnagIt1001.rar"; // 目標文件名 26 27 SFTPChannel channel = test.getSFTPChannel(); 28 ChannelSftp chSftp = channel.getChannel(sftpDetails, 60000); 29 30 /** 31 * 代碼段1 32 OutputStream out = chSftp.put(dst, new MyProgressMonitor(), ChannelSftp.OVERWRITE); // 使用OVERWRITE模式 33 byte[] buff = new byte[1024 * 256]; // 設定每次傳輸的數據塊大小爲256KB 34 int read; 35 if (out != null) { 36 System.out.println("Start to read input stream"); 37 InputStream is = new FileInputStream(src); 38 do { 39 read = is.read(buff, 0, buff.length); 40 if (read > 0) { 41 out.write(buff, 0, read); 42 } 43 out.flush(); 44 } while (read >= 0); 45 System.out.println("input stream read done."); 46 } 47 **/ 48 49 chSftp.put(src, dst, new MyProgressMonitor(), ChannelSftp.OVERWRITE); // 代碼段2 50 51 // chSftp.put(new FileInputStream(src), dst, new MyProgressMonitor(), ChannelSftp.OVERWRITE); // 代碼段3 52 53 chSftp.quit(); 54 channel.closeChannel(); 55 } 56 }
注意修改的內容僅僅是put方法,在put方法中增長了SftpProgressMonitor的實現類對象monitor做爲參數,即添加了對進度監控的支持。
運行,輸出結果以下:
1 Start to read input stream 2 Currently transferred total size: 262144 bytes 3 Currently transferred total size: 524288 bytes 4 Currently transferred total size: 786432 bytes 5 Currently transferred total size: 1048576 bytes 6 Currently transferred total size: 1310720 bytes 7 Currently transferred total size: 1572864 bytes 8 Currently transferred total size: 1835008 bytes 9 Currently transferred total size: 2097152 bytes 10 Currently transferred total size: 2359296 bytes 11 Currently transferred total size: 2621440 bytes 12 Currently transferred total size: 2883584 bytes 13 Currently transferred total size: 3145728 bytes 14 Currently transferred total size: 3407872 bytes 15 Currently transferred total size: 3670016 bytes 16 Currently transferred total size: 3848374 bytes 17 input stream read done.
固然這個SftpProgressMonitor的實現實在太簡單。JSch每次傳輸一個數據塊,就會調用count方法來實現主動進度通知。
如今咱們但願每間隔必定的時間才獲取一下文件傳輸的進度。。。看看下面的SftpProgressMonitor實現:
1 package com.longyg.sftp; 2 3 import java.text.DecimalFormat; 4 import java.util.Timer; 5 import java.util.TimerTask; 6 import com.jcraft.jsch.SftpProgressMonitor; 7 8 public class FileProgressMonitor extends TimerTask implements SftpProgressMonitor { 9 10 private long progressInterval = 5 * 1000; // 默認間隔時間爲5秒 11 12 private boolean isEnd = false; // 記錄傳輸是否結束 13 14 private long transfered; // 記錄已傳輸的數據總大小 15 16 private long fileSize; // 記錄文件總大小 17 18 private Timer timer; // 定時器對象 19 20 private boolean isScheduled = false; // 記錄是否已啓動timer記時器 21 22 public FileProgressMonitor(long fileSize) { 23 this.fileSize = fileSize; 24 } 25 26 @Override 27 public void run() { 28 if (!isEnd()) { // 判斷傳輸是否已結束 29 System.out.println("Transfering is in progress."); 30 long transfered = getTransfered(); 31 if (transfered != fileSize) { // 判斷當前已傳輸數據大小是否等於文件總大小 32 System.out.println("Current transfered: " + transfered + " bytes"); 33 sendProgressMessage(transfered); 34 } else { 35 System.out.println("File transfering is done."); 36 setEnd(true); // 若是當前已傳輸數據大小等於文件總大小,說明已完成,設置end 37 } 38 } else { 39 System.out.println("Transfering done. Cancel timer."); 40 stop(); // 若是傳輸結束,中止timer記時器 41 return; 42 } 43 } 44 45 public void stop() { 46 System.out.println("Try to stop progress monitor."); 47 if (timer != null) { 48 timer.cancel(); 49 timer.purge(); 50 timer = null; 51 isScheduled = false; 52 } 53 System.out.println("Progress monitor stoped."); 54 } 55 56 public void start() { 57 System.out.println("Try to start progress monitor."); 58 if (timer == null) { 59 timer = new Timer(); 60 } 61 timer.schedule(this, 1000, progressInterval); 62 isScheduled = true; 63 System.out.println("Progress monitor started."); 64 } 65 66 /** 67 * 打印progress信息 68 * @param transfered 69 */ 70 private void sendProgressMessage(long transfered) { 71 if (fileSize != 0) { 72 double d = ((double)transfered * 100)/(double)fileSize; 73 DecimalFormat df = new DecimalFormat( "#.##"); 74 System.out.println("Sending progress message: " + df.format(d) + "%"); 75 } else { 76 System.out.println("Sending progress message: " + transfered); 77 } 78 } 79 /** 80 * 實現了SftpProgressMonitor接口的count方法 81 */ 82 public boolean count(long count) { 83 if (isEnd()) return false; 84 if (!isScheduled) { 85 start(); 86 } 87 add(count); 88 return true; 89 } 90 /** 91 * 實現了SftpProgressMonitor接口的end方法 92 */ 93 public void end() { 94 setEnd(true); 95 System.out.println("transfering end."); 96 } 97 98 private synchronized void add(long count) { 99 transfered = transfered + count; 100 } 101 102 private synchronized long getTransfered() { 103 return transfered; 104 } 105 106 public synchronized void setTransfered(long transfered) { 107 this.transfered = transfered; 108 } 109 110 private synchronized void setEnd(boolean isEnd) { 111 this.isEnd = isEnd; 112 } 113 114 private synchronized boolean isEnd() { 115 return isEnd; 116 } 117 public void init(int op, String src, String dest, long max) { 118 // Not used for putting InputStream 119 } 120 }
再次修改SFTPTest main方法裏的put方法,改成使用新的SftpProgressMonitor的實現類對象monitor做爲參數,注意新的monitor對象的構造函數須要傳入文件大小做爲參數:
1 package com.longyg.sftp; 2 3 import java.io.File; 4 import java.util.HashMap; 5 import java.util.Map; 6 import com.jcraft.jsch.ChannelSftp; 7 8 public class SFTPTest { 9 public SFTPChannel getSFTPChannel() { 10 return new SFTPChannel(); 11 } 12 /** 13 * @param args 14 * @throws Exception 15 */ 16 public static void main(String[] args) throws Exception { 17 SFTPTest test = new SFTPTest(); 18 Map<String, String> sftpDetails = new HashMap<String, String>(); 19 // 設置主機ip,端口,用戶名,密碼 20 sftpDetails.put(SFTPConstants.SFTP_REQ_HOST, "10.9.167.55"); 21 sftpDetails.put(SFTPConstants.SFTP_REQ_USERNAME, "root"); 22 sftpDetails.put(SFTPConstants.SFTP_REQ_PASSWORD, "arthur"); 23 sftpDetails.put(SFTPConstants.SFTP_REQ_PORT, "22"); 24 25 String src = "D:\\DevSoft\\HB-SnagIt1001.rar"; // 本地文件名 26 String dst = "/home/omc/ylong/sftp/HB-SnagIt1001.rar"; // 目標文件名 27 28 SFTPChannel channel = test.getSFTPChannel(); 29 ChannelSftp chSftp = channel.getChannel(sftpDetails, 60000); 30 31 File file = new File(src); 32 long fileSize = file.length(); 33 34 /** 35 * 代碼段1 36 OutputStream out = chSftp.put(dst, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); // 使用OVERWRITE模式 37 byte[] buff = new byte[1024 * 256]; // 設定每次傳輸的數據塊大小爲256KB 38 int read; 39 if (out != null) { 40 System.out.println("Start to read input stream"); 41 InputStream is = new FileInputStream(src); 42 do { 43 read = is.read(buff, 0, buff.length); 44 if (read > 0) { 45 out.write(buff, 0, read); 46 } 47 out.flush(); 48 } while (read >= 0); 49 System.out.println("input stream read done."); 50 } 51 **/ 52 53 chSftp.put(src, dst, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); // 代碼段2 54 55 // chSftp.put(new FileInputStream(src), dst, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); // 代碼段3 56 57 chSftp.quit(); 58 channel.closeChannel(); 59 } 60 }
再次運行,結果輸出爲:
1 Try to start progress monitor. 2 Progress monitor started. 3 Transfering is in progress. 4 Current transfered: 98019 bytes 5 Sending progress message: 2.55% 6 Transfering is in progress. 7 Current transfered: 751479 bytes 8 Sending progress message: 19.53% 9 Transfering is in progress. 10 Current transfered: 1078209 bytes 11 Sending progress message: 28.02% 12 ...... 13 Transfering is in progress. 14 Current transfered: 3430665 bytes 15 Sending progress message: 89.15% 16 transfering end. 17 Transfering done. Cancel timer. 18 Try to stop progress monitor. 19 Progress monitor stoped.
如今,程序每隔5秒鐘纔會打印一下進度信息。能夠修改FileProgressMonitor類裏的progressInterval變量的值,來修改默認的間隔時間。
原文連接:http://www.cnblogs.com/longyg/archive/2012/06/25/2556576.html