jsch上傳文件到sftp服務器

http://www.cnblogs.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
複製代碼
package com.longyg.sftp;import java.util.Map;import java.util.Properties;import org.apache.log4j.Logger;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;publicclass SFTPChannel {    Session session = null;    Channel channel = null;    privatestaticfinal Logger LOG = Logger.getLogger(SFTPChannel.class.getName());    public ChannelSftp getChannel(Map<String, String> sftpDetails, int timeout) throws JSchException {        String ftpHost = sftpDetails.get(SFTPConstants.SFTP_REQ_HOST);        String port = sftpDetails.get(SFTPConstants.SFTP_REQ_PORT);        String ftpUserName = sftpDetails.get(SFTPConstants.SFTP_REQ_USERNAME);        String ftpPassword = sftpDetails.get(SFTPConstants.SFTP_REQ_PASSWORD);        int ftpPort = SFTPConstants.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對象        LOG.debug("Session created.");        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(); // 通過Session建立鏈接        LOG.debug("Session connected.");        LOG.debug("Opening Channel.");        channel = session.openChannel("sftp"); // 打開SFTP通道        channel.connect(); // 建立SFTP通道的連接        LOG.debug("Connected successfully to ftpHost = " + ftpHost + ",as ftpUserName = " + ftpUserName                + ", returning: " + channel);        return (ChannelSftp) channel;    }    publicvoid closeChannel() throws Exception {        if (channel != null) {            channel.disconnect();        }        if (session != null) {            session.disconnect();        }    }}
複製代碼

SFTPConstants是一個靜態成員變量類:

SFTPConstans.java
複製代碼
package com.longyg.sftp;publicclass SFTPConstants {    publicstaticfinal String SFTP_REQ_HOST = "host";    publicstaticfinal String SFTP_REQ_PORT = "port";    publicstaticfinal String SFTP_REQ_USERNAME = "username";    publicstaticfinal String SFTP_REQ_PASSWORD = "password";    publicstaticfinalint SFTP_DEFAULT_PORT = 22;    publicstaticfinal String SFTP_REQ_LOC = "location";}
複製代碼


文件上傳



實現文件上傳可以調用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

複製代碼
package com.longyg.sftp;import java.util.HashMap;import java.util.Map;import com.jcraft.jsch.ChannelSftp;publicclass SFTPTest {    public SFTPChannel getSFTPChannel() {        returnnew SFTPChannel();    }    /**     * @param args     * @throws Exception     */publicstaticvoid main(String[] args) throws Exception {        SFTPTest test = new SFTPTest();        Map<String, String> sftpDetails = new HashMap<String, String>();        // 設置主機ip,端口,用戶名,密碼        sftpDetails.put(SFTPConstants.SFTP_REQ_HOST, "10.9.167.55");        sftpDetails.put(SFTPConstants.SFTP_REQ_USERNAME, "root");        sftpDetails.put(SFTPConstants.SFTP_REQ_PASSWORD, "arthur");        sftpDetails.put(SFTPConstants.SFTP_REQ_PORT, "22");                String src = "D:\\DevSoft\\HB-SnagIt1001.rar"; // 本地文件名        String dst = "/home/omc/ylong/sftp/HB-SnagIt1001.rar"; // 目標文件名                      SFTPChannel channel = test.getSFTPChannel();        ChannelSftp chSftp = channel.getChannel(sftpDetails, 60000);                /**         * 代碼段1        OutputStream out = chSftp.put(dst, ChannelSftp.OVERWRITE); // 使用OVERWRITE模式        byte[] buff = new byte[1024 * 256]; // 設定每次傳輸的數據塊大小爲256KB        int read;        if (out != null) {            System.out.println("Start to read input stream");            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);            System.out.println("input stream read done.");        }        **/                chSftp.put(src, dst, ChannelSftp.OVERWRITE); // 代碼段2                // chSftp.put(new FileInputStream(src), dst, ChannelSftp.OVERWRITE); // 代碼段3                chSftp.quit();        channel.closeChannel();    }}
複製代碼

:請分別將代碼段1,代碼段2,代碼段3取消註釋,運行程序來進行測試。這三段代碼分別演示瞭如何使用JSch的不同的put方法來進行文件上傳。

代碼段1:採用向put方法返回的輸出流中寫入數據的方式來傳輸文件。 需要由程序來決定寫入什麼樣的數據,這裏是將本地文件的輸入流寫入輸出流。採用這種方式的好處是,可以自行設定每次寫入輸出流的數據塊大小,如本示例中的語句:

byte[] buff = newbyte[1024 * 256]; // 設定每次傳輸的數據塊大小爲256KB


代碼段2:直接將本地文件名爲src的文件上傳到目標服務器,目標文件名爲dst。(注:使用這個方法時,dst可以是目錄,當dst是目錄時,上傳後的目標文件名將與src文件名相同)

代碼段3:將本地文件名爲src的文件輸入流上傳到目標服務器,目標文件名爲dst。

這三段代碼實現的功能是一樣的,都是將本地的文件src上傳到了服務器的dst文件。使用時可根據具體情況選擇使用哪種實現方式。


監控傳輸進度



從前面的介紹中知道,JSch支持在文件傳輸時對傳輸進度的監控。可以實現JSch提供的SftpProgressMonitor接口來完成這個功能。

SftpProgressMonitor接口類的定義爲:

複製代碼
package com.jcraft.jsch;publicinterface SftpProgressMonitor{  publicstaticfinalint PUT=0;  publicstaticfinalint GET=1;  void init(int op, String src, String dest, long max);  boolean count(long count);  void end();}
複製代碼

init():    當文件開始傳輸時,調用init方法。

count():   當每次傳輸了一個數據塊後,調用count方法,count方法的參數爲這一次傳輸的數據塊大小。

end():     當傳輸結束時,調用end方法。

下面是一個簡單的實現:

MyProgressMonitor.java

複製代碼
package com.longyg.sftp;import com.jcraft.jsch.SftpProgressMonitor;publicclass MyProgressMonitor implements SftpProgressMonitor {    privatelong transfered;    @Override    publicboolean count(long count) {        transfered = transfered + count;        System.out.println("Currently transferred total size: " + transfered + " bytes");        returntrue;    }    @Override    publicvoid end() {        System.out.println("Transferring done.");    }    @Override    publicvoid init(int op, String src, String dest, long max) {        System.out.println("Transferring begin.");    }}
複製代碼

此時如果改變SFTPTest main方法裏調用的put方法,即可實現監控傳輸進度:

SFTPTest.java

複製代碼
package com.longyg.sftp;import java.util.HashMap;import java.util.Map;import com.jcraft.jsch.ChannelSftp;publicclass SFTPTest {    public SFTPChannel getSFTPChannel() {        returnnew SFTPChannel();    }    /**     * @param args     * @throws Exception     */publicstaticvoid main(String[] args) throws Exception {        SFTPTest test = new SFTPTest();        Map<String, String> sftpDetails = new HashMap<String, String>();        // 設置主機ip,端口,用戶名,密碼        sftpDetails.put(SFTPConstants.SFTP_REQ_HOST, "10.9.167.55");        sftpDetails.put(SFTPConstants.SFTP_REQ_USERNAME, "root");        sftpDetails.put(SFTPConstants.SFTP_REQ_PASSWORD, "arthur");        sftpDetails.put(SFTPConstants.SFTP_REQ_PORT, "22");                String src = "D:\\DevSoft\\HB-SnagIt1001.rar"; // 本地文件名        String dst = "/home/omc/ylong/sftp/HB-SnagIt1001.rar"; // 目標文件名                      SFTPChannel channel = test.getSFTPChannel();        ChannelSftp chSftp = channel.getChannel(sftpDetails, 60000);                /**         * 代碼段1        OutputStream out = chSftp.put(dst, new MyProgressMonitor(), ChannelSftp.OVERWRITE); // 使用OVERWRITE模式        byte[] buff = new byte[1024 * 256]; // 設定每次傳輸的數據塊大小爲256KB        int read;        if (out != null) {            System.out.println("Start to read input stream");            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);            System.out.println("input stream read done.");        }        **/                chSftp.put(src, dst, new MyProgressMonitor(), ChannelSftp.OVERWRITE); // 代碼段2                // chSftp.put(new FileInputStream(src), dst, new MyProgressMonitor(), ChannelSftp.OVERWRITE); // 代碼段3                chSftp.quit();        channel.closeChannel();    }}
複製代碼

注意修改的內容僅僅是put方法,在put方法中增加了SftpProgressMonitor的實現類對象monitor作爲參數,即添加了對進度監控的支持。

運行,輸出結果如下:

複製代碼
Start to read input streamCurrently transferred total size: 262144 bytesCurrently transferred total size: 524288 bytesCurrently transferred total size: 786432 bytesCurrently transferred total size: 1048576 bytesCurrently transferred total size: 1310720 bytesCurrently transferred total size: 1572864 bytesCurrently transferred total size: 1835008 bytesCurrently transferred total size: 2097152 bytesCurrently transferred total size: 2359296 bytesCurrently transferred total size: 2621440 bytesCurrently transferred total size: 2883584 bytesCurrently transferred total size: 3145728 bytesCurrently transferred total size: 3407872 bytesCurrently transferred total size: 3670016 bytesCurrently transferred total size: 3848374 bytesinput stream read done.
複製代碼

當然這個SftpProgressMonitor的實現實在太簡單。JSch每次傳輸一個數據塊,就會調用count方法來實現主動進度通知。

現在我們希望每間隔一定的時間才獲取一下文件傳輸的進度。。。看看下面的SftpProgressMonitor實現:

複製代碼
package com.longyg.sftp;import java.text.DecimalFormat;import java.util.Timer;import java.util.TimerTask;import com.jcraft.jsch.SftpProgressMonitor;publicclass FileProgressMonitor extends TimerTask implements SftpProgressMonitor {        privatelong progressInterval = 5 * 1000; // 默認間隔時間爲5秒privateboolean isEnd = false; // 記錄傳輸是否結束privatelong transfered; // 記錄已傳輸的數據總大小privatelong fileSize; // 記錄文件總大小private Timer timer; // 定時器對象privateboolean isScheduled = false; // 記錄是否已啓動timer記時器public FileProgressMonitor(long fileSize) {        this.fileSize = fileSize;    }        @Override    publicvoid run() {        if (!isEnd()) { // 判斷傳輸是否已結束            System.out.println("Transfering is in progress.");            long transfered = getTransfered();            if (transfered != fileSize) { // 判斷當前已傳輸數據大小是否等於文件總大小                System.out.println("Current transfered: " + transfered + " bytes");                sendProgressMessage(transfered);            } else {                System.out.println("File transfering is done.");                setEnd(true); // 如果當前已傳輸數據大小等於文件總大小,說明已完成,設置end            }        } else {            System.out.println("Transfering done. Cancel timer.");            stop(); // 如果傳輸結束,停止timer記時器return;        }    }        publicvoid stop() {        System.out.println("Try to stop progress monitor.");        if (timer != null) {            timer.cancel();            timer.purge();            timer = null;            isScheduled = false;        }        System.out.println("Progress monitor stoped.");    }        publicvoid start() {        System.out.println("Try to start progress monitor.");        if (timer == null) {            timer = new Timer();        }        timer.schedule(this, 1000, progressInterval);        isScheduled = true;        System.out.println("Progress monitor started.");    }        /**     * 打印progress信息     * @param transfered     */privatevoid sendProgressMessage(long transfered) {        if (fileSize != 0) {            double d = ((double)transfered * 100)/(double)fileSize;            DecimalFormat df = new DecimalFormat( "#.##");             System.out.println("Sending progress message: " + df.format(d) + "%");        } else {            System.out.println("Sending progress message: " + transfered);        }    }    /**     * 實現了SftpProgressMonitor接口的count方法     */publicboolean count(long count) {        if (isEnd()) returnfalse;        if (!isScheduled) {            start();        }        add(count);        returntrue;    }    /**     * 實現了SftpProgressMonitor接口的end方法     */publicvoid end() {        setEnd(true);        System.out.println("transfering end.");    }        privatesynchronizedvoid add(long count) {        transfered = transfered + count;    }        privatesynchronizedlong getTransfered() {        return transfered;    }        publicsynchronizedvoid setTransfered(long transfered) {        this.transfered = transfered;    }        privatesynchronizedvoid setEnd(boolean isEnd) {        this.isEnd = isEnd;    }        privatesynchronizedboolean isEnd() {        return isEnd;    }    publicvoid init(int op, String src, String dest, long max) {        // Not used for putting InputStream    }}
複製代碼

再次修改SFTPTest main方法裏的put方法,改爲使用新的SftpProgressMonitor的實現類對象monitor作爲參數,注意新的monitor對象的構造函數需要傳入文件大小作爲參數:

複製代碼
package com.longyg.sftp;import java.io.File;import java.util.HashMap;import java.util.Map;import com.jcraft.jsch.ChannelSftp;publicclass SFTPTest {    public SFTPChannel getSFTPChannel() {        returnnew SFTPChannel();    }    /**     * @param args     * @throws Exception     */publicstaticvoid main(String[] args) throws Exception {        SFTPTest test = new SFTPTest();        Map<String, String> sftpDetails = new HashMap<String, String>();        // 設置主機ip,端口,用戶名,密碼        sftpDetails.put(SFTPConstants.SFTP_REQ_HOST, "10.9.167.55");        sftpDetails.put(SFTPConstants.SFTP_REQ_USERNAME, "root");        sftpDetails.put(SFTPConstants.SFTP_REQ_PASSWORD, "arthur");        sftpDetails.put(SFTPConstants.SFTP_REQ_PORT, "22");                String src = "D:\\DevSoft\\HB-SnagIt1001.rar"; // 本地文件名        String dst = "/home/omc/ylong/sftp/HB-SnagIt1001.rar"; // 目標文件名                      SFTPChannel channel = test.getSFTPChannel();        ChannelSftp chSftp = channel.getChannel(sftpDetails, 60000);                File file = new File(src);        long fileSize = file.length();                /**         * 代碼段1        OutputStream out = chSftp.put(dst, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); // 使用OVERWRITE模式        byte[] buff = new byte[1024 * 256]; // 設定每次傳輸的數據塊大小爲256KB        int read;        if (out != null) {            System.out.println("Start to read input stream");            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);            System.out.println("input stream read done.");        }        **/                chSftp.put(src, dst, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); // 代碼段2                // chSftp.put(new FileInputStream(src), dst, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); // 代碼段3                chSftp.quit();        channel.closeChannel();    }}
複製代碼

再次運行,結果輸出爲:

複製代碼
Try to start progress monitor.Progress monitor started.Transfering is in progress.Current transfered: 98019 bytesSending progress message: 2.55%Transfering is in progress.Current transfered: 751479 bytesSending progress message: 19.53%Transfering is in progress.Current transfered: 1078209 bytesSending progress message: 28.02%......Transfering is in progress.Current transfered: 3430665 bytesSending progress message: 89.15%transfering end.Transfering done. Cancel timer.Try to stop progress monitor.Progress monitor stoped.
複製代碼

現在,程序每隔5秒鐘纔會打印一下進度信息。可以修改FileProgressMonitor類裏的progressInterval變量的值,來修改默認的間隔時間。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章