import cn.hutool.core.util.StrUtil; import com.jcraft.jsch.*; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; import org.junit.jupiter.api.Test; import java.io.*; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; @Slf4j public class DeployTest { private static final String serviceName = "quotation"; private static final String localPath = System.getProperty("user.dir") + "//target//"; private static final String remotePath = "/mnt/mobilebroken/" + serviceName + "/"; private static final String jarName = "nflg-mobilebroken-" + serviceName + "-1.0.0-SNAPSHOT.jar"; @Test public void DeployToSit() throws Exception { SSHUtil sshUtil = new SSHUtil(); sshUtil.connect("192.168.163.73", 22, "root", "NFcfs2025"); //处理主jar包 handleFile(sshUtil, localPath + jarName, remotePath + jarName); //处理字体目录 // handleDir(sshUtil, localPath, remotePath, "fonts"); //处理lib目录 // handleDir(sshUtil, localPath, remotePath, "lib"); //执行脚本启动服务 sshUtil.exec("cd " + remotePath + " && ./restart.sh"); sshUtil.disconnect(); } private void handleDir(SSHUtil sshUtil, String localPath, String remotePath, String dirName) throws Exception { printInfo("处理目录:" + dirName); List files = getFileList(localPath + dirName); for (Path file : files) { handleFile(sshUtil, file.toString(), remotePath +dirName+ "/" + file.getFileName().toString()); } printInfo("处理目录完成"); } public List getFileList(String folderPath) throws IOException { Path path = Paths.get(folderPath); if (Files.exists(path) && Files.isDirectory(path)) { List fileList = new ArrayList<>(); try (DirectoryStream stream = Files.newDirectoryStream(path)) { for (Path entry : stream) { fileList.add(entry); } } return fileList; } return null; } private void handleFile(SSHUtil sshUtil, String localPath, String remotePath) throws Exception { BasicFileAttributes localFileAttr = Files.readAttributes(Paths.get(localPath), BasicFileAttributes.class); printInfo("处理文件:{},大小:{}", localPath, getSize(localFileAttr.size())); if (sshUtil.fileExists(remotePath)) { if (!StrUtil.equals(getRemoteFileMD5(sshUtil, remotePath), getLocalFileMD5(localPath), true)) { printError("文件不一致,开始上传"); sshUtil.uploadFile(localPath, remotePath); } else { printInfo("文件一致"); } } else { printError("文件不存在,开始上传"); sshUtil.uploadFile(localPath, remotePath); } printInfo("处理完成"); } private String getRemoteFileMD5(SSHUtil sshUtil, String remotePath) throws Exception { String md5 = StrUtil.subPre(sshUtil.execWithReturn("md5sum " + remotePath), 32); printInfo("远程文件MD5为" + md5); return md5; } private String getLocalFileMD5(String localPath) throws Exception { String md5 = DigestUtils.md5Hex(new FileInputStream(localPath)); printInfo("本地文件MD5为" + md5); return md5; } private String getSize(long size) { long s = 1024; if (size < s) { return size + "B"; } s = s * 1024; if (size < s) { return String.format("%.2f", size / 1.00 / 1024) + "KB"; } s = s * 1024; if (size < s) { return String.format("%.2f", size / 1.00 / 1024 / 1024) + "MB"; } s = s * 1024; return String.format("%.2f", size / 1.00 / 1024 / 1024 / 1024) + "GB"; } private static class SSHUtil { private Session session; private ChannelSftp channelSftp; /** * 建立SSH连接 */ public void connect(String host, int port, String userName, String password) throws JSchException { printInfo("开始连接服务器,ip:{},port:{},userName:{},password:{}", host, port, userName, password); JSch jsch = new JSch(); session = jsch.getSession(userName, host, port); session.setPassword(password); session.setConfig("StrictHostKeyChecking", "no"); session.connect(); printInfo("连接成功"); Channel channel = session.openChannel("sftp"); channel.connect(); channelSftp = (ChannelSftp) channel; } /** * 执行远程命令并返回输出 */ public void exec(String command) { printInfo("开始执行命令:{}", command); if (session == null || !session.isConnected()) { throw new IllegalStateException("SSH未连接"); } ChannelExec channel = null; SshResultCallback callback = new SshResultCallback() { @Override public void onOutput(String output) { printInfo(false, output); } @Override public void onErrorOutput(String errorOutput) { printError(false, errorOutput); } @Override public void onCompleted(int exitStatus) { printInfo("输出完毕,退出状态:" + exitStatus); } @Override public void onError(Exception e) { printError(false, e.getMessage()); } }; try { // 创建执行命令的通道 channel = (ChannelExec) session.openChannel("exec"); channel.setCommand(command); // 获取输入流以读取命令输出 InputStream in = channel.getInputStream(); InputStream err = channel.getExtInputStream(); channel.connect(); // 读取命令输出 BufferedReader reader = new BufferedReader(new InputStreamReader(in)); BufferedReader errReader = new BufferedReader(new InputStreamReader(err)); String line; while ((line = reader.readLine()) != null) { callback.onOutput(line); } // 读取错误输出 String errLine; while ((errLine = errReader.readLine()) != null) { callback.onErrorOutput(errLine); } // 获取退出状态 int exitStatus = channel.getExitStatus(); callback.onCompleted(exitStatus); } catch (JSchException | IOException e) { callback.onError(e); } finally { if (channel != null) { channel.disconnect(); } if (session != null) { session.disconnect(); } printInfo("执行命令完毕"); } } public String execWithReturn(String command) throws Exception { printInfo("开始执行命令:{}", command); if (session == null || !session.isConnected()) { throw new IllegalStateException("SSH未连接"); } ChannelExec channel = null; BufferedReader reader = null; try { // 创建执行命令的通道 channel = (ChannelExec) session.openChannel("exec"); channel.setCommand(command); // 获取输入流以读取命令输出 InputStream in = channel.getInputStream(); InputStream err = channel.getExtInputStream(); channel.connect(); reader = new BufferedReader(new InputStreamReader(in)); String data = reader.readLine(); // log.info("执行命令成功,返回数据:" + data); BufferedReader errReader = new BufferedReader(new InputStreamReader(err)); String errData = errReader.readLine(); // log.info("执行命令失败,返回数据:" + errData); if (StrUtil.isBlank(errData)) { return data; } else { errReader.close(); throw new Exception("执行命令失败:" + errData); } } finally { if (channel != null) { channel.disconnect(); } if (reader != null) { try { reader.close(); } catch (Exception ignored) { } } printInfo("执行命令完毕"); } } /** * 检查远程文件是否存在 */ public boolean fileExists(String remotePath) throws SftpException { try { channelSftp.stat(remotePath); return true; } catch (SftpException e) { if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { return false; } throw e; } } /** * 上传文件 */ public void uploadFile(String localPath, String remotePath) throws SftpException { printInfo("开始上传本地文件{}到远程{}", localPath, remotePath); channelSftp.put(localPath, remotePath); printInfo("上传完成"); } /** * 关闭连接 */ public void disconnect() { if (channelSftp != null) { channelSftp.disconnect(); } if (session != null) { session.disconnect(); } } } public interface SshResultCallback { void onOutput(String output); void onErrorOutput(String errorOutput); void onCompleted(int exitStatus); void onError(Exception e); } private static void printError(String msg) { if (StrUtil.isNotBlank(msg)) { System.out.println(red + msg + reset); } } private static void printError(boolean addTime, String msg) { if (addTime) { System.out.println(red + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + msg + reset); } else { System.out.println(red + msg + reset); } } private static void printInfo(String msg) { printInfo(true, msg); } private static void printInfo(boolean addTime, String msg) { if (addTime) { System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + msg); } else { System.out.println(msg); } } private static void printInfo(String format, Object... args) { printInfo(true, format, args); } private static void printInfo(boolean addTime, String format, Object... args) { if (addTime) { System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + StrUtil.format(format, args)); } else { System.out.println(StrUtil.format(format, args)); } } public static final String red = "\u001B[31m"; public static final String reset = "\u001B[0m"; }