参考 :
java实现scp功能本地与远程服务器之间的文件传输_优快云_Hzx的博客-优快云博客
运行环境
- java8
- shell
- maven
项目代码
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>org.example</groupId>
<artifactId>auto-deploy</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-scp</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>ch.ethz.ganymed</groupId>
<artifactId>ganymed-ssh2</artifactId>
<version>build210</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
</dependencies>
<build>
<plugins><plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- 指定该Main Class为全局的唯一入口 -->
<mainClass>cn.nordrassil.auto.deploy.AutoDeploy</mainClass>
<layout>ZIP</layout>
<includeSystemScope>true</includeSystemScope><!-- 把第三方jar包打进去 -->
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal><!--可以把依赖的包都打包到生成的Jar包中 -->
</goals>
</execution>
</executions>
</plugin></plugins>
</build>
</project>
AutoDeploy.java
package cn.nordrassil.auto.deploy;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.SCPClient;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.io.*;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: liyue
* @Date: 2021/10/21/19:19
* @Description:
*/
public class AutoDeploy {
public static void main(String[] args) throws Exception {
long time = System.currentTimeMillis();
String config;
if (args.length > 0) {
config = args[0];
} else {
config = System.getProperty("user.dir") + "/config.json";
}
JSONObject jsonObject = JSONObject.parseObject(readJsonFile(config));
if (jsonObject != null) {
System.out.println(join("配置文件加载成功"));
}
String ip = jsonObject.getString("ip");
String username = jsonObject.getString("username");
String password = jsonObject.getString("password");
JSONArray packageCommands = jsonObject.getJSONArray("packageCommands");
String uploadFile = jsonObject.getString("uploadFile");
JSONArray remoteServerCommands = jsonObject.getJSONArray("remoteServerCommands");
// 创建临时shell执行打包命令
String tmpFile = "tmp_" + time + ".sh";
exec("touch " + tmpFile);
for (int i = 0; i < packageCommands.size(); i++) {
append(packageCommands.getString(i), tmpFile);
}
System.out.println(join("开始打包"));
exec("sh " + tmpFile);
exec("rm -rf " + tmpFile);
System.out.println(join("打包完成"));
System.out.println(join("开始上传程序包"));
long t2 = System.currentTimeMillis();
putFile(username, password, ip, uploadFile, "/root");
System.out.println(join("上传程序包完成,耗时:{}秒", round((System.currentTimeMillis() - t2) / 1000.0, 2)));
StringBuffer rscs = new StringBuffer();
for (int i = 0; i < remoteServerCommands.size(); i++) {
rscs.append(remoteServerCommands.getString(i)).append(";");
}
rscs.delete(rscs.length() - 1, rscs.length());
System.out.println(join("执行远程命令"));
run(ip, 22, username, password, rscs.toString());
System.out.println(join("远程命令执行完成"));
System.out.println();
System.out.println(join("执行完成,耗时:{}秒", round((System.currentTimeMillis() - time) / 1000.0, 2)));
}
public static Double round(Double data, int amount) {
if (data == null)
return null;
//利用BigDecimal来实现四舍五入.保留一位小数
double result = new BigDecimal(data).setScale(amount, BigDecimal.ROUND_HALF_UP).doubleValue();
//1代表保留1位小数,保留两位小数就是2,依此累推
//BigDecimal.ROUND_HALF_UP 代表使用四舍五入的方式
return result;
}
public static boolean putFile(String userName, String password, String ipAddr, String loadFilePath, String clusterPath) {
boolean isAuthed = false;
try {
if (InetAddress.getByName(ipAddr).isReachable(1500)) {
Connection conn = new Connection(ipAddr);
conn.connect();
isAuthed = conn.authenticateWithPassword(userName, password);
if (isAuthed) {
SCPClient scpClient = conn.createSCPClient();
scpClient.put(loadFilePath, clusterPath);
conn.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
return isAuthed;
}
public static String join(String str, Object... param) {
for (Object p : param) {
str = str.replaceFirst("\\{\\}", p.toString());
}
return getNow() + " " + str;
}
/**
* 锁对象
*/
private static final Object lockObj = new Object();
/**
* 存放不同的日期模板格式的sdf的Map
*/
private static Map<String, ThreadLocal<SimpleDateFormat>> sdfMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();
/**
* 返回一个ThreadLocal的sdf,每个线程只会new一次sdf
*
* @param pattern
* @return
*/
private static SimpleDateFormat getSdf(final String pattern) {
ThreadLocal<SimpleDateFormat> tl = sdfMap.get(pattern);
// 此处的双重判断和同步是为了防止sdfMap这个单例被多次put重复的sdf
if (tl == null) {
synchronized (lockObj) {
tl = sdfMap.get(pattern);
if (tl == null) {
// 只有Map中还没有这个pattern的sdf才会生成新的sdf并放入map
System.out.println("put new sdf of pattern " + pattern + " to map");
// 这里是关键,使用ThreadLocal<SimpleDateFormat>替代原来直接new SimpleDateFormat
tl = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
//System.out.println("thread: " + Thread.currentThread() + " init pattern: " + pattern);
return new SimpleDateFormat(pattern);
}
};
sdfMap.put(pattern, tl);
}
}
}
return tl.get();
}
public static String getNow() {
return getSdf("yyyy-MM-dd HH:mm:ss").format(new Date());
}
public static String exec(String command) {
try {
String returnString = "";
Process pro = null;
Runtime runTime = Runtime.getRuntime();
if (runTime == null) {
System.err.println("Create runtime false! command:" + command);
}
pro = runTime.exec(command);
BufferedReader input = new BufferedReader(new InputStreamReader(pro.getInputStream()));
PrintWriter output = new PrintWriter(new OutputStreamWriter(pro.getOutputStream()));
String line;
while ((line = input.readLine()) != null) {
System.out.println(line);
}
input.close();
output.close();
pro.destroy();
return returnString;
} catch (IOException ex) {
System.err.println(join("执行命令失败,命令:{}", command, ex));
return null;
}
}
public static void append(String conent, String path) {
FileWriter fw = null;
try {
notExistsCreate(path);
File f = new File(path);
fw = new FileWriter(f, true);
} catch (IOException e) {
e.printStackTrace();
}
PrintWriter pw = new PrintWriter(fw);
pw.println(conent);
pw.flush();
try {
fw.flush();
pw.close();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void notExistsCreate(String path) {
try {
File file = new File(path);
if (!file.exists()) {
File parentDir = new File(file.getParent());
if (!parentDir.exists()) {
parentDir.mkdirs();
}
file.createNewFile();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static String readJsonFile(String fileName) {
String jsonStr = "";
try {
File jsonFile = new File(fileName);
FileReader fileReader = new FileReader(jsonFile);
Reader reader = new InputStreamReader(new FileInputStream(jsonFile), "utf-8");
int ch = 0;
StringBuffer sb = new StringBuffer();
while ((ch = reader.read()) != -1) {
sb.append((char) ch);
}
fileReader.close();
reader.close();
jsonStr = sb.toString();
return jsonStr;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public static void run(String hostname, int port, String username, String password, String command) throws Exception {
Connection conn = new Connection(hostname, port);
conn.connect();
conn.authenticateWithPassword(username, password);
Session ssh = conn.openSession();
ssh.execCommand(command);
InputStream is = new StreamGobbler(ssh.getStdout());
BufferedReader brs = new BufferedReader(new InputStreamReader(is));
while (true) {
String line = brs.readLine();
if (line == null) {
break;
}
System.out.println(line);
}
}
}
config.json
{
"ip": "192.168.1.1",
"username": "root",
"password": "123456",
"packageCommands": [
"cd /Users/test/helloworld",
"mvn clean package"
],
"uploadFile": "/Users/test/helloworld/target/helloworld.jar",
"remoteServerCommands": [
"cd /home/helloworld",
"rm -rf helloworld.jar",
"mv /home/helloworld.jar ./",
"./install.sh"
]
}
说明 :
packageCommands : 本地打包命令列表
uploadFile : 打包后的jar包文件,该文件会上传到服务器
remoteServerCommands : jar包上传到服务器之后执行的命令列表