在這篇文中要展示如何用 Java 以 JSch 這個 library 來進行 SSH 連線,
首先是用 Maven 引入 library :
<!-- https://mvnrepository.com/artifact/com.jcraft/jsch --> <dependency> <groupId>com.jcraft</groupId> <artifactId>jsch</artifactId> <version>0.1.55</version> </dependency>接著先直接上程式碼:
package test; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.JSch; import com.jcraft.jsch.Session; import com.jcraft.jsch.SftpATTRS; import com.jcraft.jsch.SftpException; public class SftpTest { public static void main(String[] args) throws Exception { String privateKeyPath = "D:\\...\xx.pem"; //your private key file path String userName = "xxName"; //your username to access target server String host = "xxx.xxx.xxx.xxx"; //hostname of target server int port = 22; //target server port Session session = null; try (InputStream inputStreamOfPrivateKey = new FileInputStream(new File(privateKeyPath))){ JSch jsch = new JSch(); jsch.addIdentity(privateKeyPath, inputStreamOfPrivateKey.readAllBytes(), null, null); session = jsch.getSession(userName, host, port); //session.setPassword("xxxPassword"); session.setConfig("StrictHostKeyChecking", "no"); session.setTimeout(60000); session.connect(); ChannelSftp channelSftp = (ChannelSftp) session.openChannel("sftp"); channelSftp.connect(); //check if file exists System.out.println(isFileExist(channelSftp, "/xxx/xxx.png")); //check file attributes SftpATTRS fileAttrs = channelSftp.lstat("/xxx/xxx.png"); System.out.println(fileAttrs.getSize()); //create directory mkdir(channelSftp, "/xxx/xxx"); //upload file upload(channelSftp, "D:\\xxx\\xxx.jpg", "/xxx/xxx"); channelSftp.exit(); }catch(Exception e) { e.printStackTrace(); } finally { if (session != null && session.isConnected()) { session.disconnect(); } } System.out.println("Done"); } public static void upload(ChannelSftp channelSftp, String srcFilePath, String targetDirectoryPath) { targetDirectoryPath = targetDirectoryPath.replace("\\", "/"); File srcFile = new File(srcFilePath); try (FileInputStream fileInputStream = new FileInputStream(srcFile)){ if (isFileExist(channelSftp, targetDirectoryPath)) { channelSftp.cd(targetDirectoryPath); }else { channelSftp.cd("/"); //go to root path String[] targetDirectoryNameList = targetDirectoryPath.split("/"); for(String targetDirectoryName : targetDirectoryNameList) { if ("".equals(targetDirectoryName)) { continue; } if (!isFileExist(channelSftp, targetDirectoryName)) { channelSftp.mkdir(targetDirectoryName); } channelSftp.cd(targetDirectoryName); } } channelSftp.put(fileInputStream, srcFile.getName()); } catch (IOException | SftpException e) { e.printStackTrace(); } } static void mkdir(ChannelSftp channelSftp, String directoryPath) { directoryPath = directoryPath.replace("\\", "/"); try { channelSftp.cd("/"); //go to root path String[] directoryNameList = directoryPath.split("/"); for(String directoryName : directoryNameList) { if ("".equals(directoryName)) { continue; } if (!isFileExist(channelSftp, directoryName)) { channelSftp.mkdir(directoryName); } channelSftp.cd(directoryName); } } catch (SftpException e) { e.printStackTrace(); } } static boolean isFileExist(ChannelSftp channelSftp, String filePath) { boolean isFileExist = false; try { channelSftp.lstat(filePath); isFileExist = true; } catch (SftpException e) { if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { isFileExist = false; } } return isFileExist; } }說明:
上面程式碼的情境是想要 SSH 連線至一台 Linux Server,
且已經設定好 ssh public key 並放在 server 的 .ssh 資料夾裡,
而我們本地的機器上也已經有一個對應的 ssh private key,
所以我們只需使用 private key 來進行連線而不用使用密碼。
session.setConfig("StrictHostKeyChecking", "no");
這行程式碼設定了 StrictHostKeyChecking 為 no,
是因為在 SSH 連線時有時會需要終端機互動,
例如在使用命令式模式指令進行 SSH 連線操作時,
server 可能會跳出一些訊息要你輸入 "yes" 等動作才能進行下一步,
設定 StrictHostKeyChecking 為 no 就可以避免這種互動式操作,
可參考:
在程式碼中可以看到,取得了 ChannelSftp 實例後,
就可以使用它來進行對 server 的操作,例如: cd, pwd, ls, mkdir, ... 等,
就像我們平台用命令列模式指令一樣,當使用了 cd 指令後,
會把我們帶到特定的 server 上的檔案路徑位置,
並且可以以目前所在的位置用相對路徑進行 ls, mkdir, ... 等操作,
當然也可使用絕對路徑進行操作。
在上述程式碼中,
我包裝並實作了 isFileExist(), upload(), mkdir() 等 methild
來取代直接使用 ChannelSftp 自身的 API,
在實作的 method 中限定了檔案路徑參數必須為絕對路徑以避免不必要的
路徑處理麻煩,
isFileExist() 使用了 ChannelSftp.lstat() 和 ChannelSftp.SSH_FX_NO_SUCH_FILE 的補獲來實作。
upload() 和 mkdir() 使用 isFileExist() 來判斷路徑上資料夾的存在與否,
如果不存在的話則幫忙建立相應的資料夾。