文章來源:http://m.survivalescaperooms.com/longyg/archive/2012/06/25/2556576.html
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支持三種文件傳輸模式:| OVERWRITE | 完全覆蓋模式,這是JSch的默認文件傳輸模式,即如果目標文件已經存在,傳輸的文件將完全覆蓋目標文件,產生新的文件。 |
| RESUME | 恢復模式,如果文件已經傳輸一部分,這時由于網絡或其他任何原因導致文件傳輸中斷,如果下一次傳輸相同的文件, 則會從上一次中斷的地方續傳。 |
| APPEND | 追加模式,如果目標文件已存在,傳輸的文件將在目標文件后追加。 |
| 創建ChannelSftp對象 |
編寫一個工具類,根據ip,用戶名及密碼得到一個SFTP channel對象,即ChannelSftp的實例對象,在應用程序中就可以使用該對象來調用SFTP的各種操作方法。
SFTPChannel.java按 Ctrl+C 復制代碼按 Ctrl+C 復制代碼SFTPConstants是一個靜態成員變量類:
SFTPConstans.java按 Ctrl+C 復制代碼按 Ctrl+C 復制代碼| 文件上傳 |
實現文件上傳可以調用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, SftpProgressMonitor monitor, int mode) | 將本地文件名為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, SftpProgressMonitor monitor, int mode) | 將本地的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開始寫入數據。 |
應用實例:
SFTPTest.java
按 Ctrl+C 復制代碼按 Ctrl+C 復制代碼注:請分別將代碼段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 SftpProgressMonitor.java 2 3 package com.jcraft.jsch; 4 5 public interface SftpProgressMonitor{ 6 public static final int PUT=0; 7 public static final int GET=1; 8 void init(int op, String src, String dest, long max); 9 boolean count(long count);10 void end();11 }SftpProgressMonitorinit():當文件開始傳輸時,調用init方法。
count(): 當每次傳輸了一個數據塊后,調用count方法,count方法的參數為這一次傳輸的數據塊大小。
end():當傳輸結束時,調用end方法。
下面是一個簡單的實現:

1 MyProgressMonitor.java 2 3 package com.longyg.sftp; 4 5 import com.jcraft.jsch.SftpProgressMonitor; 6 7 public class MyProgressMonitor implements SftpProgressMonitor { 8 private long transfered; 9 10 @Override11 public boolean count(long count) {12 transfered = transfered + count;13 System.out.println("Currently transferred total size: " + transfered + " bytes");14 return true;15 }16 17 @Override18 public void end() {19 System.out.println("Transferring done.");20 }21 22 @Override23 public void init(int op, String src, String dest, long max) {24 System.out.println("Transferring begin.");25 }26 }MyProgressMonitor.java此時如果改變SFTPTest main方法里調用的put方法,即可實現監控傳輸進度:

1 SFTPTest.java 2 3 package com.longyg.sftp; 4 5 import java.util.HashMap; 6 import java.util.Map; 7 8 import com.jcraft.jsch.ChannelSftp; 9 10 public class SFTPTest {11 12 public SFTPChannel getSFTPChannel() {13 return new SFTPChannel();14 }15 16 /**17 * @param args18 * @throws Exception19 */20 public static void main(String[] args) throws Exception {21 SFTPTest test = new SFTPTest();22 23 Map<String, String> sftpDetails = new HashMap<String, String>();24 // 設置主機ip,端口,用戶名,密碼25 sftpDetails.put(SFTPConstants.SFTP_REQ_HOST, "10.9.167.55");26 sftpDetails.put(SFTPConstants.SFTP_REQ_USERNAME, "root");27 sftpDetails.put(SFTPConstants.SFTP_REQ_PASSWORD, "arthur");28 sftpDetails.put(SFTPConstants.SFTP_REQ_PORT, "22");29 30 String src = "D://DevSoft//HB-SnagIt1001.rar"; // 本地文件名31 String dst = "/home/omc/ylong/sftp/HB-SnagIt1001.rar"; // 目標文件名32 33 SFTPChannel channel = test.getSFTPChannel();34 ChannelSftp chSftp = channel.getChannel(sftpDetails, 60000);35 36 /**37 * 代碼段138 OutputStream out = chSftp.put(dst, new MyProgressMonitor(), ChannelSftp.OVERWRITE); // 使用OVERWRITE模式39 byte[] buff = new byte[1024 * 256]; // 設定每次傳輸的數據塊大小為256KB40 int read;41 if (out != null) {42 System.out.println("Start to read input stream");43 InputStream is = new FileInputStream(src);44 do {45 read = is.read(buff, 0, buff.length);46 if (read > 0) {47 out.write(buff, 0, read);48 }49 out.flush();50 } while (read >= 0);51 System.out.println("input stream read done.");52 }53 **/54 55 chSftp.put(src, dst, new MyProgressMonitor(), ChannelSftp.OVERWRITE); // 代碼段256 57 // chSftp.put(new FileInputStream(src), dst, new MyProgressMonitor(), ChannelSftp.OVERWRITE); // 代碼段358 59 chSftp.quit();60 channel.closeChannel();61 }62 }SFTPTest.java
1 logs 2 3 Start to read input stream 4 Currently transferred total size: 262144 bytes 5 Currently transferred total size: 524288 bytes 6 Currently transferred total size: 786432 bytes 7 Currently transferred total size: 1048576 bytes 8 Currently transferred total size: 1310720 bytes 9 Currently transferred total size: 1572864 bytes10 Currently transferred total size: 1835008 bytes11 Currently transferred total size: 2097152 bytes12 Currently transferred total size: 2359296 bytes13 Currently transferred total size: 2621440 bytes14 Currently transferred total size: 2883584 bytes15 Currently transferred total size: 3145728 bytes16 Currently transferred total size: 3407872 bytes17 Currently transferred total size: 3670016 bytes18 Currently transferred total size: 3848374 bytes19 input stream read done.logs
當然這個SftpProgressMonitor的實現實在太簡單。JSch每次傳輸一個數據塊,就會調用count方法來實現主動進度通知。
現在我們希望每間隔一定的時間才獲取一下文件傳輸的進度。。。看看下面的SftpProgressMonitor實現:

1 FileProgressMonitor.java 2 3 package com.longyg.sftp; 4 5 import java.text.DecimalFormat; 6 import java.util.Timer; 7 import java.util.TimerTask; 8 9 import com.jcraft.jsch.SftpProgressMonitor; 10 11 public class FileProgressMonitor extends TimerTask implements SftpProgressMonitor { 12 13 private long progressInterval = 5 * 1000; // 默認間隔時間為5秒 14 15 private boolean isEnd = false; // 記錄傳輸是否結束 16 17 private long transfered; // 記錄已傳輸的數據總大小 18 19 private long fileSize; // 記錄文件總大小 20 21 private Timer timer; // 定時器對象 22 23 private boolean isScheduled = false; // 記錄是否已啟動timer記時器 24 25 public FileProgressMonitor(long fileSize) { 26 this.fileSize = fileSize; 27 } 28 29 @Override 30 public void run() { 31 if (!isEnd()) { // 判斷傳輸是否已結束 32 System.out.println("Transfering is in progress."); 33 long transfered = getTransfered(); 34 if (transfered != fileSize) { // 判斷當前已傳輸數據大小是否等于文件總大小 35 System.out.println("Current transfered: " + transfered + " bytes"); 36 sendProgressMessage(transfered); 37 } else { 38 System.out.println("File transfering is done."); 39 setEnd(true); // 如果當前已傳輸數據大小等于文件總大小,說明已完成,設置end 40 } 41 } else { 42 System.out.println("Transfering done. Cancel timer."); 43 stop(); // 如果傳輸結束,停止timer記時器 44 return; 45 } 46 } 47 48 public void stop() { 49 System.out.println("Try to stop progress monitor."); 50 if (timer != null) { 51 timer.cancel(); 52 timer.purge(); 53 timer = null; 54 isScheduled = false; 55 } 56 System.out.println("Progress monitor stoped."); 57 } 58 59 public void start() { 60 System.out.println("Try to start progress monitor."); 61 if (timer == null) { 62 timer = new Timer(); 63 } 64 timer.schedule(this, 1000, progressInterval); 65 isScheduled = true; 66 System.out.println("Progress monitor started."); 67 } 68 69 /** 70 * 打印progress信息 71 * @param transfered 72 */ 73 private void sendProgressMessage(long transfered) { 74 if (fileSize != 0) { 75 double d = ((double)transfered * 100)/(double)fileSize; 76 DecimalFormat df = new DecimalFormat( "#.##"); 77 System.out.println("Sending progress message: " + df.format(d) + "%"); 78 } else { 79 System.out.println("Sending progress message: " + transfered); 80 } 81 } 82 83 /** 84 * 實現了SftpProgressMonitor接口的count方法 85 */ 86 public boolean count(long count) { 87 if (isEnd()) return false; 88 if (!isScheduled) { 89 start(); 90 } 91 add(count); 92 return true; 93 } 94 95 /** 96 * 實現了SftpProgressMonitor接口的end方法 97 */ 98 public void end() { 99 setEnd(true);100 System.out.println("transfering end.");101 }102 103 private synchronized void add(long count) {104 transfered = transfered + count;105 }106 107 private synchronized long getTransfered() {108 return transfered;109 }110 111 public synchronized void setTransfered(long transfered) {112 this.transfered = transfered;113 }114 115 private synchronized void setEnd(boolean isEnd) {116 this.isEnd = isEnd;117 }118 119 private synchronized boolean isEnd() {120 return isEnd;121 }122 123 public void init(int op, String src, String dest, long max) {124 // Not used for putting InputStream125 }126 }FileProgressMonitor 再次修改SFTPTest main方法里的put方法,改為使用新的SftpProgressMonitor的實現類對象monitor作為參數,注意新的monitor對象的構造函數需要傳入文件大小作為參數:

1 SFTPTest.java 2 3 package com.longyg.sftp; 4 5 import java.io.File; 6 import java.util.HashMap; 7 import java.util.Map; 8 9 import com.jcraft.jsch.ChannelSftp;10 11 public class SFTPTest {12 13 public SFTPChannel getSFTPChannel() {14 return new SFTPChannel();15 }16 17 /**18 * @param args19 * @throws Exception20 */21 public static void main(String[] args) throws Exception {22 SFTPTest test = new SFTPTest();23 24 Map<String, String> sftpDetails = new HashMap<String, String>();25 // 設置主機ip,端口,用戶名,密碼26 sftpDetails.put(SFTPConstants.SFTP_REQ_HOST, "10.9.167.55");27 sftpDetails.put(SFTPConstants.SFTP_REQ_USERNAME, "root");28 sftpDetails.put(SFTPConstants.SFTP_REQ_PASSWORD, "arthur");29 sftpDetails.put(SFTPConstants.SFTP_REQ_PORT, "22");30 31 String src = "D://DevSoft//HB-SnagIt1001.rar"; // 本地文件名32 String dst = "/home/omc/ylong/sftp/HB-SnagIt1001.rar"; // 目標文件名33 34 SFTPChannel channel = test.getSFTPChannel();35 ChannelSftp chSftp = channel.getChannel(sftpDetails, 60000);36 37 File file = new File(src);38 long fileSize = file.length();39 40 /**41 * 代碼段142 OutputStream out = chSftp.put(dst, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); // 使用OVERWRITE模式43 byte[] buff = new byte[1024 * 256]; // 設定每次傳輸的數據塊大小為256KB44 int read;45 if (out != null) {46 System.out.println("Start to read input stream");47 InputStream is = new FileInputStream(src);48 do {49 read = is.read(buff, 0, buff.length);50 if (read > 0) {51 out.write(buff, 0, read);52 }53 out.flush();54 } while (read >= 0);55 System.out.println("input stream read done.");56 }57 **/58 59 chSftp.put(src, dst, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); // 代碼段260 61 // chSftp.put(new FileInputStream(src), dst, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); // 代碼段362 63 chSftp.quit();64 channel.closeChannel();65 }66 }SFTPTest 再次運行,結果輸出為:

1 logs 2 3 Try to start progress monitor. 4 Progress monitor started. 5 Transfering is in progress. 6 Current transfered: 98019 bytes 7 Sending progress message: 2.55% 8 Transfering is in progress. 9 Current transfered: 751479 bytes10 Sending progress message: 19.53%11 Transfering is in progress.12 Current transfered: 1078209 bytes13 Sending progress message: 28.02%14 ......15 Transfering is in progress.16 Current transfered: 3430665 bytes17 Sending progress message: 89.15%18 transfering end.19 Transfering done. Cancel timer.20 Try to stop progress monitor.21 Progress monitor stoped.logs
現在,程序每隔5秒鐘才會打印一下進度信息。可以修改FileProgressMonitor類里的progressInterval變量的值,來修改默認的間隔時間。
新聞熱點
疑難解答