JSch是Java Secure Channel的縮寫驱犹。JSch是一個SSH2的純Java實現(xiàn)匹厘。它允許你連接到一個SSH服務(wù)器,并且可以使用端口轉(zhuǎn)發(fā)维哈,X11轉(zhuǎn)發(fā)绳姨,文件傳輸?shù)龋?dāng)然你也可以集成它的功能到你自己的應(yīng)用程序阔挠。
本文只介紹如何使用JSch實現(xiàn)的SFTP功能飘庄。
SFTP是Secure File Transfer Protocol的縮寫,安全文件傳送協(xié)議购撼」蛳鳎可以為傳輸文件提供一種安全的加密方法。SFTP 為 SSH的一部份迂求,是一種傳輸文件到服務(wù)器的安全方式碾盐。SFTP是使用加密傳輸認(rèn)證信息和傳輸?shù)臄?shù)據(jù),所以揩局,使用SFTP是非常安全的毫玖。但是,由于這種傳輸方式使用了加密/解密技術(shù),所以傳輸效率比普通的FTP要低得多付枫,如果您對網(wǎng)絡(luò)安全性要求更高時烹玉,可以使用SFTP代替FTP。(來自百度的解釋)
要使用JSch阐滩,需要下載它的jar包春霍,請從官網(wǎng)下載它:http://www.jcraft.com/jsch/
ChannelSftp類是JSch實現(xiàn)SFTP核心類,它包含了所有SFTP的方法叶眉,如:
put(): 文件上傳
get(): 文件下載
cd(): 進(jìn)入指定目錄
ls(): 得到指定目錄下的文件列表
rename(): 重命名指定文件或目錄
rm(): 刪除指定文件
mkdir(): 創(chuàng)建目錄
rmdir(): 刪除目錄
等等(這里省略了方法的參數(shù)址儒,put和get都有多個重載方法,具體請看源代碼衅疙,這里不一一列出莲趣。)
JSch支持三種文件傳輸模式:
傳輸模式名 | 描述 |
---|---|
OVERWRITE | 完全覆蓋模式,這是JSch的默認(rèn)文件傳輸模式饱溢,即如果目標(biāo)文件已經(jīng)存在喧伞,傳輸?shù)奈募⑼耆采w目標(biāo)文件,產(chǎn)生新的文件绩郎。 |
RESUME | 恢復(fù)模式潘鲫,如果文件已經(jīng)傳輸一部分,這時由于網(wǎng)絡(luò)或其他任何原因?qū)е挛募鬏斨袛嗬哒龋绻乱淮蝹鬏斚嗤奈募嚷兀瑒t會從上一次中斷的地方續(xù)傳。 |
APPEND | 追加模式状植,如果目標(biāo)文件已存在浊竟,傳輸?shù)奈募⒃谀繕?biāo)文件后追加。 |
1津畸、引入jar包
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
2振定、實際例子
package com.bluemoon.executor.core.executor;
import com.bluemoon.executor.core.log.XxlJobLogger;
import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.nio.charset.Charset;
public class JSchExecutor {
private static Logger log = LoggerFactory.getLogger(JSchExecutor.class);
private String charset = "UTF-8"; // 設(shè)置編碼格式
private String user; // 用戶名
private String passwd; // 登錄密碼
private String host; // 主機IP
private int port = 22; //默認(rèn)端口
private JSch jsch;
private Session session;
private ChannelSftp sftp;
/**
*
* @param user 用戶名
* @param passwd 密碼
* @param host 主機IP
*/
public JSchExecutor(String user, String passwd, String host ) {
this.user = user;
this.passwd = passwd;
this.host = host;
}
/**
*
* @param user 用戶名
* @param passwd 密碼
* @param host 主機IP
*/
public JSchExecutor(String user, String passwd, String host , int port ) {
this.user = user;
this.passwd = passwd;
this.host = host;
this.port = port;
}
/**
* 連接到指定的IP
*
* @throws JSchException
*/
public void connect() throws JSchException {
jsch = new JSch();
session = jsch.getSession(user, host, port);
session.setPassword(passwd);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
Channel channel = session.openChannel("sftp");
channel.connect();
sftp = (ChannelSftp) channel;
log.info("連接到SFTP成功。host: " + host);
}
/**
* 關(guān)閉連接
*/
public void disconnect(){
if (sftp != null && sftp.isConnected()) {
sftp.disconnect();
}
if(session != null && session.isConnected()){
session.disconnect();
}
}
/**
* 執(zhí)行一條命令
*/
public int execCmd(String command) throws Exception{
XxlJobLogger.log( "開始執(zhí)行命令:" + command);
int returnCode = -1;
BufferedReader reader = null;
Channel channel = null;
channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
channel.setInputStream(null);
((ChannelExec) channel).setErrStream(System.err);
InputStream in = channel.getInputStream();
reader = new BufferedReader(new InputStreamReader(in));//中文亂碼貌似這里不能控制肉拓,看連接的服務(wù)器的
channel.connect();
System.out.println("The remote command is: " + command);
String buf ;
while ((buf = reader.readLine()) != null) {
XxlJobLogger.log(buf);
}
reader.close();
// Get the return code only after the channel is closed.
if (channel.isClosed()) {
returnCode = channel.getExitStatus();
}
XxlJobLogger.log( "Exit-status:" + returnCode );
/* StringBuffer buf = new StringBuffer( 1024 );
byte[] tmp = new byte[ 1024 ];
while ( true ) {
while ( in.available() > 0 ) {
int i = in.read( tmp, 0, 1024 );
if ( i < 0 ) break;
buf.append( new String( tmp, 0, i ) );
}
if ( channel.isClosed() ) {
res = channel.getExitStatus();
XxlJobLogger.log( "Exit-status:" + res );
System.out.println( "Exit-status:" + res );
break;
}
TimeUnit.MILLISECONDS.sleep(100);
}
XxlJobLogger.log( buf.toString() );*/
channel.disconnect();
return returnCode;
}
/**
* 執(zhí)行相關(guān)的命令
*/
public void execCmd() {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String command = "";
BufferedReader reader = null;
Channel channel = null;
try {
while ((command = br.readLine()) != null) {
channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
channel.setInputStream(null);
((ChannelExec) channel).setErrStream(System.err);
channel.connect();
InputStream in = channel.getInputStream();
reader = new BufferedReader(new InputStreamReader(in,
Charset.forName(charset)));
String buf = null;
while ((buf = reader.readLine()) != null) {
System.out.println(buf);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSchException e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
channel.disconnect();
}
}
/**
* 上傳文件
*/
public void uploadFile(String local,String remote) throws Exception {
File file = new File(local);
if (file.isDirectory()) {
throw new RuntimeException(local + " is not a file");
}
InputStream inputStream = null;
try {
String rpath = remote.substring(0,remote.lastIndexOf("/")+1);
if (!isDirExist(rpath)){
createDir(rpath);
}
inputStream = new FileInputStream(file);
sftp.setInputStream(inputStream);
sftp.put(inputStream, remote);
} catch (Exception e) {
throw e;
}finally{
if(inputStream != null){
inputStream.close();
}
}
}
/**
* 下載文件
*/
public void downloadFile(String remote,String local) throws Exception{
OutputStream outputStream = null;
try {
sftp.connect(5000);
outputStream = new FileOutputStream(new File(local));
sftp.get(remote, outputStream);
outputStream.flush();
} catch (Exception e) {
throw e;
}finally{
if(outputStream != null){
outputStream.close();
}
}
}
/**
* 移動到相應(yīng)的目錄下
* @param pathName 要移動的目錄
* @return
*/
public boolean changeDir(String pathName){
if(pathName == null || pathName.trim().equals("")){
log.debug("invalid pathName");
return false;
}
try {
sftp.cd(pathName.replaceAll("\\\\", "/"));
log.debug("directory successfully changed,current dir=" + sftp.pwd());
return true;
} catch (SftpException e) {
log.error("failed to change directory",e);
return false;
}
}
/**
* 創(chuàng)建一個文件目錄后频,mkdir每次只能創(chuàng)建一個文件目錄
* 或者可以使用命令mkdir -p 來創(chuàng)建多個文件目錄
*/
public void createDir(String createpath) {
try {
if (isDirExist(createpath)) {
sftp.cd(createpath);
return;
}
String pathArry[] = createpath.split("/");
StringBuffer filePath = new StringBuffer("/");
for (String path : pathArry) {
if (path.equals("")) {
continue;
}
filePath.append(path + "/");
if (isDirExist(filePath.toString())) {
sftp.cd(filePath.toString());
} else {
// 建立目錄
sftp.mkdir(filePath.toString());
// 進(jìn)入并設(shè)置為當(dāng)前目錄
sftp.cd(filePath.toString());
}
}
sftp.cd(createpath);
} catch (SftpException e) {
throw new RuntimeException("創(chuàng)建路徑錯誤:" + createpath);
}
}
/**
* 判斷目錄是否存在
* @param directory
* @return
*/
public boolean isDirExist(String directory)
{
boolean isDirExistFlag = false;
try
{
SftpATTRS sftpATTRS = sftp.lstat(directory);
isDirExistFlag = true;
return sftpATTRS.isDir();
}
catch (Exception e)
{
if (e.getMessage().toLowerCase().equals("no such file"))
{
isDirExistFlag = false;
}
}
return isDirExistFlag;
}
}
3、用法
public static void main(String[] args) {
JSchExecutor jSchUtil = new JSchExecutor( "user", "password#","192.168.243.21");
try {
jSchUtil.connect();
jSchUtil.uploadFile("C:\\data\\applogs\\bd-job\\jobhandler\\2020-03-07\\employee.py","/data/applogs/bd-job-777/jobhandler/2018-09-15/employee.py");
//jSchUtil.execCmd("python /data/bluemoon/kettle/runScript/ods/fact_org_employee.py 'so you is wahek 哈哈哈快遞放假塑料袋放進(jìn)了'");
jSchUtil.execCmd("python /data/applogs/bd-job/jobhandler/gluesource/employee.py 中文名稱");
//jSchUtil.execCmd("cat /data/applogs/bd-job/jobhandler/test");
} catch (Exception e) {
e.printStackTrace();
}finally {
jSchUtil.disconnect();
}
}
4暖途、實時返回打印的日志
在實際開發(fā)中卑惜,需要執(zhí)行一個python文檔,里面含有多條的python語句丧肴,可能耗時1個小時才能執(zhí)行完残揉,這種情況需要能夠?qū)崟r的返回日志信息胧后,看看python到底執(zhí)行到哪一步芋浮。
使用ChannelShell可與服務(wù)器保持連接狀態(tài),可交互信息
用法:
/**
* 實時打印日志信息
*/
public int shellCmd(String command) throws Exception{
XxlJobLogger.log( "開始執(zhí)行命令:" + command);
int returnCode = -1;
ChannelShell channel=(ChannelShell) session.openChannel("shell");
InputStream in = channel.getInputStream();
channel.setPty(true);
channel.connect();
OutputStream os = channel.getOutputStream();
os.write((command + "\r\n").getBytes());
os.write("exit\r\n".getBytes());
os.flush();
XxlJobLogger.log("The remote command is:{}" ,command);
byte[] tmp=new byte[1024];
while(true) {
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0) break;
XxlJobLogger.log(new String(tmp, 0, i));
}
if (channel.isClosed()) {
if (in.available() > 0) continue;
returnCode = channel.getExitStatus();
XxlJobLogger.log("exit-status: " + channel.getExitStatus());
break;
}
try{Thread.sleep(1000);}catch(Exception ee){}
}
os.close();
in.close();
channel.disconnect();
session.disconnect();
return returnCode;
}
參考:https://www.cnblogs.com/longyg/archive/2012/06/25/2556576.html