原創(chuàng)性聲明:本文完全為筆者原創(chuàng),請尊重筆者勞動力。轉(zhuǎn)載務(wù)必注明原文地址。
sftp與ftp
要談sftp
(SSH File Transfer Protocol
),首先要談ftp
(File 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方法就有很多重載。還支持進度顯示。