使用Jsch上传文件到linux或执行命令行
什么是Jsch ?
JSch 是SSH2的一个纯Java实现。它允许你连接到一个sshd 服务器,使用端口转发,X11转发,文件传输等等。你可以将它的功能集成到你自己的 程序中。同时该项目也提供一个J2ME版本用来在手机上直连SSHD服务器。
本文目的 ?
由于工作的原因涉及到从本地上传多个文件到远程服务器上,上传之后并执行远程服务器上的脚本,为了实现这些步骤的自动化,所以做了些技术尝试。
如何实现?
很简单 贴代码。
1. 创建Maven项目,导入相关依赖。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.XXX.rapid.deploy</groupId> <artifactId>rapid-deploy</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>rapid-deploy</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> <dependency> <groupId>com.jcraft</groupId> <artifactId>jsch</artifactId> <version>0.1.48</version></dependency> </dependencies></project>
2. 用TDD的方式开发,我们先写一个连接服务器的测试。
package com.XXX.rapid.deploy;import static org.hamcrest.CoreMatchers.is;import static org.hamcrest.CoreMatchers.nullValue;import static org.junit.Assert.assertThat;import org.junit.Before;import org.junit.Test;import com.jcraft.jsch.Session;public class SSHSessionTest {private SSHSession sshSession;@Beforepublic void setUp() {sshSession = new SSHSession();}@Testpublic void shouldAcceptIfUserLoinInfoIsCorrect() throws Exception {// WhenSession session = sshSession.openSession("wenhao", "1qaz!QAZ","10.0.0.6", 22);// ThenassertThat(session.isConnected(), is(true));}@Testpublic void shouldRejectIfUserLoginInfoIsWrong() throws Exception {// WhenSession session = sshSession.openSession("Wrong Username", "password","host", 0);// ThenassertThat(session, nullValue());}}3. 它的实现类。
package com.XXX.rapid.deploy;import java.util.Properties;import com.jcraft.jsch.JSch;import com.jcraft.jsch.JSchException;import com.jcraft.jsch.Session;public class SSHSession {private static final int TIMEOUT = 3000;public Session openSession(String username, String password, String host,int port) {Session session = null;try {session = connect(username, password, host, port);} catch (JSchException e) {System.out.println("[ERROR] " + e.getMessage()+ ", check your username and password.");}return session;}private Session connect(String username, String password, String host,int port) throws JSchException {Session session = new JSch().getSession(username, host, port);session = skipHostKeyChecking(session);session.setPassword(password);session.connect(TIMEOUT);return session;}private Session skipHostKeyChecking(Session session) {Properties config = new Properties();config.put("StrictHostKeyChecking", "no");session.setConfig(config);return session;}}4. 测试执行命令行。
package com.XXX.rapid.deploy;import static org.hamcrest.CoreMatchers.is;import static org.junit.Assert.assertThat;import org.junit.Before;import org.junit.Test;import com.jcraft.jsch.Session;public class ShellExecuterTest {private SSHSession sshSession;private ShellExecuter shellExecuter;@Beforepublic void setUp() {sshSession = new SSHSession();Session session = sshSession.openSession("wenhao", "1qaz!QAZ","10.0.0.6", 22);shellExecuter = new ShellExecuter(session);}@Testpublic void shouldAcceptWhenExecuteCommand() throws Exception {// GivenString command = "cd upload;sh deploy.sh";// WhenString result = shellExecuter.execute(command);System.out.println(result);// ThenassertThat(result.trim(), is("This is a bash script"));}}5. 执行命令行实现类。
package com.XXX.rapid.deploy;import java.io.IOException;import java.io.InputStream;import com.jcraft.jsch.Channel;import com.jcraft.jsch.ChannelExec;import com.jcraft.jsch.JSchException;import com.jcraft.jsch.Session;public class ShellExecuter {private static final int BLOCK_SIZE = 1024;private Session session;public ShellExecuter(Session session) {this.session = session;}private ChannelExec openChannelExec(Session session) {ChannelExec channelExec = null;try {Channel channel = session.openChannel("exec");channelExec = (ChannelExec) channel;} catch (JSchException e) {e.printStackTrace();}return channelExec;}public String execute(String command) throws IOException, JSchException {ChannelExec channelExec = openChannelExec(session);StringBuffer buffer = executeCommand(command, channelExec);closeChannelExec(channelExec);return buffer.toString();}private StringBuffer executeCommand(String command, ChannelExec channelExec)throws IOException, JSchException {InputStream result = channelExec.getInputStream();channelExec.setCommand(command);channelExec.connect();StringBuffer buffer = generateResult(result);return buffer;}private StringBuffer generateResult(InputStream inputStream)throws IOException {StringBuffer buffer = new StringBuffer();byte[] bytes = new byte[BLOCK_SIZE];while (inputStream.read(bytes) > 0) {buffer.append(new String(bytes));}return buffer;}private void closeChannelExec(ChannelExec channelExec) {channelExec.disconnect();}}6. 测试文件上传。
package com.XXX.rapid.deploy;import static org.hamcrest.CoreMatchers.is;import static org.junit.Assert.assertThat;import java.io.File;import org.junit.Before;import org.junit.Test;import com.jcraft.jsch.Session;public class FileSenderTest {private SSHSession sshSession;private FileSender fileSender;@Beforepublic void setUp() {sshSession = new SSHSession();Session session = sshSession.openSession("wenhao", "1qaz!QAZ","10.0.0.6", 22);fileSender = new FileSender(session);}@Testpublic void shouldAcceptWhenUploadFileToRemoteServer() throws Exception {// Given// Whenboolean isUploaded = fileSender.upload("/home/wenhao", new File("demo.txt"));boolean isFileUploaded = fileSender.upload("/home/wenhao/upload", new File("demo.txt"));// ThenassertThat(isUploaded, is(true));assertThat(isFileUploaded, is(true));}@Testpublic void shouldRejectIfUserDontHaveWritePremissionOnRemoteDirectory()throws Exception {// Given// Whenboolean isFileUploaded = fileSender.upload("/home", new File("demo.txt"));// ThenassertThat(isFileUploaded, is(false));}@Testpublic void shouldRejectIfLoaclFileIsNotExist() throws Exception {// Whenboolean isUploaded = fileSender.upload("/home/wenhao", new File("demo2.txt"));// ThenassertThat(isUploaded, is(false));}@Testpublic void shouldRejectIfRemoteDirectoryIsNotExist() throws Exception {// Whenboolean isUploaded = fileSender.upload("/home/wenhao/notexitstdir", new File("demo.txt"));// ThenassertThat(isUploaded, is(false));}}7. 文件上传的实现类。
package com.XXX.rapid.deploy;import java.io.File;import java.io.FileInputStream;import java.util.Vector;import com.jcraft.jsch.Channel;import com.jcraft.jsch.ChannelSftp;import com.jcraft.jsch.ChannelSftp.LsEntry;import com.jcraft.jsch.JSchException;import com.jcraft.jsch.Session;import com.jcraft.jsch.SftpException;public class FileSender {private Session session;public FileSender(Session session) {this.session = session;}public ChannelSftp openChannelSftp(Session session) {ChannelSftp channelSftp = null;try {Channel channel = session.openChannel("sftp");channel.connect();channelSftp = (ChannelSftp) channel;} catch (JSchException e) {e.printStackTrace();}return channelSftp;}public boolean isChangeedDirectory(String path, ChannelSftp channelSftp) {try {channelSftp.cd(path);return true;} catch (Exception e) {System.out.println("[ERROR] " + path + ": No such directory");}return false;}public boolean upload(String directory, File uploadFile) {boolean isUploaded = false;ChannelSftp channelSftp = openChannelSftp(session);if (isChangeedDirectory(directory, channelSftp)) {isUploaded = uploadFile(directory, uploadFile, channelSftp);}return isUploaded;}private boolean uploadFile(String directory, File uploadFile,ChannelSftp channelSftp) {boolean isUploadedFile = false;try {channelSftp.put(new FileInputStream(uploadFile),uploadFile.getName());isUploadedFile = isFileUploaded(directory, uploadFile, channelSftp);} catch (Exception e) {System.out.println("[ERROR] " + e.getMessage());}return isUploadedFile;}@SuppressWarnings("unchecked")private boolean isFileUploaded(String directory, File uploadFile,ChannelSftp channelSftp) throws SftpException {boolean isFileUploaded = false;Vector<LsEntry> files = channelSftp.ls(directory);for (LsEntry file : files) {isFileUploaded |= isFileExist(uploadFile, file);}return isFileUploaded;}private boolean isFileExist(File uploadFile, LsEntry file) {return uploadFile.getName().equals(file.getFilename()) ? true : false;}}
用Jsch比较方便的一点就是平台独立性,适用于不同的操作系统,而且也相当的简单。但是还有更简单的方法,第一种就是使用ant脚本,ant只是jsch;第二种使用gradle脚本,其实也是调用的ant;第三种是用Groovy语言写,groovy集成了ant功能,但是毕竟是动态语言灵活性比较好,推荐使用。本人代码水平不高,还望看客多给意见,有问题请mail我devhup@gmail.com.