sftp的純java實現(xiàn)——jsch

原創(chuàng)性聲明:本文完全為筆者原創(chuàng),請尊重筆者勞動力。轉(zhuǎn)載務(wù)必注明原文地址。

sftp與ftp

要談sftpSSH File Transfer Protocol),首先要談ftpFile Transfer Protocol),大家都知道ftp是文件傳輸協(xié)議,它基于tcp協(xié)議,可以用來發(fā)送文件。剛開始學(xué)web開發(fā)的時候,接觸到一些免費的云空間,當時就是用的一個ftp工具把項目傳上去的。

sftp與ssh

sftp,就是安全(security)的ftp,因為它是基于ssh協(xié)議,ssh是一個安全協(xié)議,用來在不同系統(tǒng)或者服務(wù)器之間進行安全連接,ssh在連接和傳送的過程中會加密所有的數(shù)據(jù)。所以通俗的來講,通過ssh協(xié)議進行文件傳輸,那就是sftp。

那么如何使用ssh來實現(xiàn)文件傳輸呢?熟悉linux的伙伴們應(yīng)該對ssh也不陌生,因為linux自帶了ssh(噓!其實我并不熟悉linux,只是知道linux自帶了ssh,以前裝了沒多久就卸了,現(xiàn)在發(fā)現(xiàn)會linux逼格會提高不少。一定要再裝一個玩一下!)。

遺憾的是,ssh基本上是基于linux和一些客戶端安裝軟件。那么在我們平常的web開發(fā)中,要用sftp來傳輸文件怎么辦呢?jsch就是解決辦法了。

jsch簡介

jsch是ssh的純java實現(xiàn)。這么講有點抽象,通俗說,你在官網(wǎng)上down下來就是一個jar包,引入你的項目,就可以用來給一個同樣開啟了ssh服務(wù)的服務(wù)器安全的傳文件了(當然,你需要那臺目標服務(wù)器的一些用戶名和密碼信息,不然就gg了)。

開始使用

  • 第一步:首先在maven中央倉庫中查一下怎么在pom中依賴,可以點這里

tip: 如果你用的是Gradle等其它構(gòu)建工具,就用其他方式依賴進項目。如果沒用構(gòu)件工具,直接把jar包添加到項目里吧。

maven的是這個(我用的是當前最新版本0.1.54):

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

加到pom文件中就可以進行下一步了。

  • 第二步:創(chuàng)建一個工具類:SFTPUtils.java, 內(nèi)容如下
import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Properties;

/**
 * SFTP工具類
 * 包含兩個方法:
 * 一個創(chuàng)建一個sftp通道對象,并返回這個對象,通過這 個對象可以直接發(fā)送文件。
 * 另一個是用于關(guān)閉回話和通道的。
 */
public class SFTPUtils {
  static private final Logger log = LoggerFactory.getLogger(SFTPUtils.class);

  static private Session session = null;
  static private Channel channel = null;
  static private int timeout = 60000; //超時數(shù),一分鐘

  /**
   * 傳入一個通道對象
   * @param username 遠程要連接的服務(wù)器的用戶名
   * @param password 遠程要連接的服務(wù)器的密碼
   * @param ip 遠程服務(wù)器ip
   * @param port 遠程服務(wù)器的ssh服務(wù)端口
   * @return ChannelSftp返回指向這個通道指定的地址的channel實例
   * @throws JSchException
   */
  public static ChannelSftp getChannel(String username, String password, String ip, String port) throws JSchException {
    JSch jsch = new JSch(); // 創(chuàng)建JSch對象
    // 根據(jù)用戶名,主機ip,端口獲取一個Session對象
    session = jsch.getSession(username, ip, Integer.valueOf(aisle.getServerPort()));
    log.info("Session created...");
    if (password != null) {
      session.setPassword(password); // 設(shè)置密碼
    }
    Properties config = new Properties();
    config.put("StrictHostKeyChecking", "no");
    session.setConfig(config); // 為Session對象設(shè)置properties
    session.setTimeout(timeout); // 設(shè)置timeout時間
    session.connect(); // 通過Session建立鏈接
    log.info("Session connected, Opening Channel...");
    channel = session.openChannel("sftp"); // 打開SFTP通道
    channel.connect(); // 建立SFTP通道的連接
    log.info("Connected successfully to ip :{}, ftpUsername is :{}, return :{}",
        ip,username, channel);
    return (ChannelSftp) channel;
  }

  /**
   * 關(guān)閉channel和session
   * @throws Exception
   */
  public static void closeChannel() throws Exception {
    if (channel != null) {
      channel.disconnect();
    }
    if (session != null) {
      session.disconnect();
    }
  }
}
  • 第三步:工具類都建好了,就直接用吧——創(chuàng)建測試類。
import com.gildata.gup.util.SFTPUtils;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.SftpException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.util.Vector;

/**
 * sftp推送測試類
 */
@Service
public class SftpTest {

  private final Logger log = LoggerFactory.getLogger(SftpPushTest.class);

  /**
   * 文件推送的測試方法。
   * destDirPath 遠程服務(wù)器要保存的文件夾路徑
   * file  本地要推送的文件對象
   * username 遠程服務(wù)器的用戶名
   * password  遠程服務(wù)器的密碼
   * ip  遠程服務(wù)器ip
   * port 遠程服務(wù)器ssh服務(wù)端口
   */
  public void testSftp() throws SftpException {
      // 假設(shè)參數(shù)值
      String dstDirPath = "E:\\target";
      String username = "admin";
      String password = "admin";
      String ip = "127.0.0.1"; 
      String port = 21; 

      ChannelSftp channelSftp = null;
      String dstFilePath; // 目標文件名(帶路徑),如: D:\\file\\file.doc,這個路徑應(yīng)該是遠程目標服務(wù)器下要保存的路徑
      try {
          // 一、 獲取channelSftp對象
          channelSftp = SFTPUtils.getChannel(username, password, ip, port);
          // 二、 判斷遠程路徑dstDirPath是否存在(通道配置的路徑)
          try {
              Vector dir = channelSftp.ls(dstDirPath);
              if (dir == null) { // 如果路徑不存在,則創(chuàng)建
                  channelSftp.mkdir(dstDirPath);
              }
          } catch (SftpException e) { // 如果dstDirPath不存在,則會報錯,此時捕獲異常并創(chuàng)建dstDirPath路徑
              channelSftp.mkdir(dstDirPath); // 此時創(chuàng)建路o如果再報錯,即創(chuàng)建失敗,則拋出異常
              e.printStackTrace();
          }
          // 三、 推送文件
          try {
              log.info("send the file : {}", file.getName());
              dstFilePath = dstDirPath + "\\" + file.getName();
              log.info("the file all path is :{}", dstFilePath);
              // 推送: dstFilePath——傳送過去的文件路徑(全路徑),采用默認的覆蓋式推送
              channelSftp.put(new FileInputStream(file), dstFilePath); // jsch觸發(fā)推送操作的方法
          }  catch (SftpException e) {
              log.debug("An error occurred during sftp push, send data fail, the target path is :{}", dstDirPath);
              if (log.isDebugEnabled()) {
                  e.printStackTrace();
              }
          }
      }  finally {
          // 處理后事
          if (channelSftp != null)
              channelSftp.quit();
          try {
              SFTPUtils.closeChannel();
          } catch (Exception e) {
              if (log.isDebugEnabled())
                  e.printStackTrace();
          }
      }
  }

}

執(zhí)行testSftp方法,就可以把file文件傳到目標服務(wù)器的dstDirPath目錄下了。

假設(shè)file文件在本地的路徑為: D:\\source\\sftp_learning.ppt。而目標路徑dstDirPath為: E:\\target,那么執(zhí)行推送后,將會在ip為ip的遠程設(shè)備下的E:\\target目錄下找到sftp_learning.ppt文件。

問題?!

不過遺憾的是,window并不像linux一樣自帶了ssh服務(wù)。像上面的E:\\target這樣的目錄顯然表明了這個遠程設(shè)備是window系統(tǒng)。正常開發(fā)中,即使你的用戶名、 密碼、 端口都沒有輸錯,程序也將會拋SftpException異常,那是因為你得目標服務(wù)器沒有啟用ssh服務(wù)。

怎么解決呢?

既然目標服務(wù)器是沒有自帶ssh服務(wù)的window,那就想辦法在window下配置ssh服務(wù)咯。

一般而言,服務(wù)器通常跑在linux下,所以不用擔心這個問題。筆者這次也是因為想在自己的window下本地測試一下,所以遇到了這個問題。如何在window下配置ssh服務(wù),這又是另一個話題了。這次測試中,我用的是Cygwin工具。具體怎么使用,網(wǎng)上一搜一大把。如果讀著支持筆者,就請關(guān)注我吧,我會盡快把Cygwin的使用心得分享給大家的!

關(guān)于jsch實現(xiàn)sftp傳輸文件的分享就這么些了。希望對大家有幫助!(正好22點22分,OMG)

更多關(guān)于ChannelSftp的API方法可以參見官網(wǎng),或者百度一下。就put方法就有很多重載。還支持進度顯示。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容