国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

WebSocket+xterm+springboot+vue 實現(xiàn) xshell 操作linux終端功能

這篇具有很好參考價值的文章主要介紹了WebSocket+xterm+springboot+vue 實現(xiàn) xshell 操作linux終端功能。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

效果圖

WebSocket+xterm+springboot+vue 實現(xiàn) xshell 操作linux終端功能

1.工具介紹與安裝

1.1 xterm.js

xterm 是一個使用 TypeScript 編寫的前端終端組件,可以直接在瀏覽器中實現(xiàn)一個命令行終端應(yīng)用。Xterm.js 適用于大多數(shù)終端應(yīng)用程序,如 bash,vim 和 tmux,這包括對基于curses的應(yīng)用程序和鼠標(biāo)事件的支持。

1.2 安裝

// 1、安裝 xterm
npm install --save xterm

// 2、安裝xterm-addon-fit
// xterm.js的插件,使終端的尺寸適合包含元素。
npm install --save xterm-addon-fit

// 3、安裝xterm-addon-attach(這個你不用就可以不裝)
// xterm.js的附加組件,用于附加到Web Socket
npm install --save xterm-addon-attach

安裝完之后可以在package.json看到依賴
WebSocket+xterm+springboot+vue 實現(xiàn) xshell 操作linux終端功能

1.3 websocket

websocket主要用于將前端的指令傳遞到后端,后端做出響應(yīng)在傳回前端顯示。

springboot中安裝依賴

 <!--  WebSocket 支持  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

2.前端實現(xiàn)

2.1 模板部分

//菜單中
<template>
      <div class="console" id="terminal" style="min-height: cala(100vh)"></div>
</template>
//這種是直接vue里面
<template>
  <section>
    <div id="log" style="margin:10px auto;">
      <div class="console" id="terminal"></div>
    </div>
  </section>
</template> 

2.2 邏輯部分

這一部分是填寫你后端連接的地址,注意不要弄錯

const WebSocketUrl = "wss://localhost:8080/ws/ssh";
<script>
import "xterm/css/xterm.css";
import { Terminal } from "xterm";
import { FitAddon } from "xterm-addon-fit";
import { AttachAddon } from "xterm-addon-attach";

export default {
  name: "Xterm",
  props: {
    socketURI: {
      type: String,
      default: ""
    }
  },
  data () {
    return {
      term: null,
      socket: null,
      rows: 32,
      cols: 20,
      SetOut: false,
      isKey: false,
    };
  },
  mounted () {
    this.initSocket();

  },
  beforeDestroy () {
    this.socket.close();
    // this.term.dispose();
  },
  methods: {
    //Xterm主題
    initTerm () {
      const term = new Terminal({
        rendererType: "canvas", //渲染類型
        rows: this.rows, //行數(shù)
        // cols: this.cols,// 設(shè)置之后會輸入多行之后覆蓋現(xiàn)象
        convertEol: true, //啟用時,光標(biāo)將設(shè)置為下一行的開頭
        // scrollback: 10,//終端中的回滾量
        fontSize: 14, //字體大小
        disableStdin: false, //是否應(yīng)禁用輸入。
        cursorStyle: "block", //光標(biāo)樣式
        // cursorBlink: true, //光標(biāo)閃爍
        scrollback: 30,
        tabStopWidth: 4,
        theme: {
          foreground: "yellow", //字體
          background: "#060101", //背景色
          cursor: "help" //設(shè)置光標(biāo)
        }
      });
      const attachAddon = new AttachAddon(this.socket);
      const fitAddon = new FitAddon();
      term.loadAddon(attachAddon);
      term.loadAddon(fitAddon);
     
      term.open(document.getElementById("terminal"));
     
      term.focus();
      let _this = this;
      //限制和后端交互,只有輸入回車鍵才顯示結(jié)果
      term.prompt = () => {
        term.write("\r\n$ ");
      };
      term.prompt();
      
      function runFakeTerminal (_this) {
        if (term._initialized) {
          return;
        }
        // 初始化
        term._initialized = true;
        term.writeln();//控制臺初始化報錯處
        term.prompt();
        // / **
        //     *添加事件監(jiān)聽器,用于按下鍵時的事件。事件值包含
        //     *將在data事件以及DOM事件中發(fā)送的字符串
        //     *觸發(fā)了它。
        //     * @返回一個IDisposable停止監(jiān)聽。
        //  * /
        //   / ** 更新:xterm 4.x(新增)
        //  *為數(shù)據(jù)事件觸發(fā)時添加事件偵聽器。發(fā)生這種情況
        //  *用戶輸入或粘貼到終端時的示例。事件值
        //  *是`string`結(jié)果的結(jié)果,在典型的設(shè)置中,應(yīng)該通過
        //  *到支持pty。
        //  * @返回一個IDisposable停止監(jiān)聽。
        //  * /
        // 支持輸入與粘貼方法
        term.onData(function (key) {
          let order = {
            Data: key,
            Op: "stdin"
          };
          _this.onSend(order);
        });
        _this.term = term;
      }
      runFakeTerminal(_this);
    },
    //webShell主題
    initSocket () {
     // const WebSocketUrl = "ws://localhost:8080/ws/ssh";
      const WebSocketUrl = "wss://localhost:8080/ws/ssh";
      this.socket = new WebSocket(
        WebSocketUrl
      );
      this.socketOnClose(); //關(guān)閉
      this.socketOnOpen(); //
      this.socketOnError();
    },
    //webshell鏈接成功之后操作
    socketOnOpen () {
      this.socket.onopen = () => {
        // 鏈接成功后
        this.initTerm();
      };
    },
    //webshell關(guān)閉之后操作
    socketOnClose () {
      this.socket.onclose = () => {
        console.log("close socket");
      };
    },
    //webshell錯誤信息
    socketOnError () {
      this.socket.onerror = () => {
        console.log("socket 鏈接失敗");
      };
    },
    //特殊處理
    onSend (data) {
      data = this.base.isObject(data) ? JSON.stringify(data) : data;
      data = this.base.isArray(data) ? data.toString() : data;
      data = data.replace(/\\\\/, "\\");
      this.shellWs.onSend(data);
    },
    //刪除左右兩端的空格
    trim (str) {
      return str.replace(/(^\s*)|(\s*$)/g, "");
    }
  }
};
</script>

2.3 樣式 以及自適應(yīng)屏幕大小

這一部分是因為xterm.js的FitAddon 只會橫向的去適應(yīng)屏幕大小,縱向他會有留白。這里是覆蓋了xterm的一個原來的樣式,下面的scope里面的是對字體的一些修飾

<style>
.xterm-screen{
  min-height: calc(100vh);
}
</style>

WebSocket+xterm+springboot+vue 實現(xiàn) xshell 操作linux終端功能

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
.xterm-screen{
  min-height: calc(100vh);
}
</style>
<style scoped>
 h1, h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style> 

3.后端實現(xiàn)

3.1 實體類部分

    /**
     *
     * @Description SshModel實體類
     */
    public class SshModel {

        private String name;
        private String host;
        private Integer port;
        private String user;
        private String password;
        /**
         * 編碼格式
         */
        private String charset;

        /**
         * 文件目錄
         */
        private String fileDirs;

        /**
         * ssh 私鑰
         */
        private String privateKey;

        private String connectType;

        /**
         * 不允許執(zhí)行的命令
         */
        private String notAllowedCommand;

        /**
         * 允許編輯的后綴文件
         */
        private String allowEditSuffix;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getNotAllowedCommand() {
            return notAllowedCommand;
        }

        public void setNotAllowedCommand(String notAllowedCommand) {
            this.notAllowedCommand = notAllowedCommand;
        }

        public ConnectType connectType() {
            return EnumUtil.fromString(ConnectType.class, this.connectType, ConnectType.PASS);
        }

        public String getConnectType() {
            return connectType;
        }

        public void setConnectType(String connectType) {
            this.connectType = connectType;
        }

        public String getPrivateKey() {
            return privateKey;
        }

        public void setPrivateKey(String privateKey) {
            this.privateKey = privateKey;
        }

        public String getFileDirs() {
            return fileDirs;
        }

        public void setFileDirs(String fileDirs) {
            this.fileDirs = fileDirs;
        }

        public List<String> fileDirs() {
            return StringUtil.jsonConvertArray(this.fileDirs, String.class);
        }

        public void fileDirs(List<String> fileDirs) {
            if (fileDirs != null) {
                for (int i = fileDirs.size() - 1; i >= 0; i--) {
                    String s = fileDirs.get(i);
                    fileDirs.set(i, FileUtil.normalize(s));
                }
                this.fileDirs = JSONArray.toJSONString(fileDirs);
            } else {
                this.fileDirs = null;
            }
        }

        public String getHost() {
            return host;
        }

        public void setHost(String host) {
            this.host = host;
        }

        public Integer getPort() {
            return port;
        }

        public void setPort(Integer port) {
            this.port = port;
        }

        public String getUser() {
            return user;
        }

        public void setUser(String user) {
            this.user = user;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public String getCharset() {
            return charset;
        }

        public void setCharset(String charset) {
            this.charset = charset;
        }

        public Charset getCharsetT() {
            Charset charset;
            try {
                charset = Charset.forName(this.getCharset());
            } catch (Exception e) {
                charset = CharsetUtil.CHARSET_UTF_8;
            }
            return charset;
        }

        public List<String> allowEditSuffix() {
            return StringUtil.jsonConvertArray(this.allowEditSuffix, String.class);
        }

        public void allowEditSuffix(List<String> allowEditSuffix) {
            if (allowEditSuffix == null) {
                this.allowEditSuffix = null;
            } else {
                this.allowEditSuffix = JSONArray.toJSONString(allowEditSuffix);
            }
        }

        public String getAllowEditSuffix() {
            return allowEditSuffix;
        }

        public void setAllowEditSuffix(String allowEditSuffix) {
            this.allowEditSuffix = allowEditSuffix;
        }

        /**
         * 檢查是否包含禁止命令
         *
         * @param sshItem   實體
         * @param inputItem 輸入的命令
         * @return false 存在禁止輸入的命令
         */
        public static boolean checkInputItem(SshModel sshItem, String inputItem) {
            // 檢查禁止執(zhí)行的命令
            String notAllowedCommand = StrUtil.emptyToDefault(sshItem.getNotAllowedCommand(), StrUtil.EMPTY).toLowerCase();
            if (StrUtil.isEmpty(notAllowedCommand)) {
                return true;
            }
            List<String> split = Arrays.asList(StrUtil.split(notAllowedCommand, StrUtil.COMMA));
            inputItem = inputItem.toLowerCase();
            List<String> commands = Arrays.asList(StrUtil.split(inputItem, StrUtil.CR));
            commands.addAll(Arrays.asList(StrUtil.split(inputItem, "&")));
            for (String s : split) {
                //
                boolean anyMatch = commands.stream().anyMatch(item -> StrUtil.startWithAny(item, s + StrUtil.SPACE, ("&" + s + StrUtil.SPACE), StrUtil.SPACE + s + StrUtil.SPACE));
                if (anyMatch) {
                    return false;
                }
                //
                anyMatch = commands.stream().anyMatch(item -> StrUtil.equals(item, s));
                if (anyMatch) {
                    return false;
                }
            }
            return true;
        }

        public enum ConnectType {
            /**
             * 賬號密碼
             */
            PASS,
            /**
             * 密鑰
             */
            PUBKEY
        }
    }

3.1.1 先配置一個WebSocketConfig類

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class WebSocketConfig implements ServletContextInitializer {
    /**
     * 給spring容器注入這個ServerEndpointExporter對象
     * 相當(dāng)于xml:
     * <beans>
     * <bean id="serverEndpointExporter" class="org.springframework.web.socket.server.standard.ServerEndpointExporter"/>
     * </beans>
     * <p>
     * 檢測所有帶有@serverEndpoint注解的bean并注冊他們。
     *
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        servletContext.addListener(WebAppRootListener.class);
        servletContext.setInitParameter("org.apache.tomcat.websocket.textBufferSize","52428800");
        servletContext.setInitParameter("org.apache.tomcat.websocket.binaryBufferSize","52428800");
    }
}

3.2 SshHander類

主要的實現(xiàn)是這兩個核心類,這里是通過ssh去連接服務(wù)器的。
這里填寫你的服務(wù)器用戶名,密碼等

@OnOpen
        public void onOpen(javax.websocket.Session session) throws Exception {
            SessionSet.add(session);
            SshModel sshItem = new SshModel();
            sshItem.setHost("xxxx");
            sshItem.setPort(xxxx);
            sshItem.setUser("xxxx");
            sshItem.setPassword("xxxxx");
            int cnt = OnlineCount.incrementAndGet(); // 在線數(shù)加1
            log.info("有連接加入,當(dāng)前連接數(shù)為:{},sessionId={}", cnt,session.getId());
            SendMessage(session, "連接成功,sessionId="+session.getId());
            HandlerItem handlerItem = new HandlerItem(session, sshItem);
            handlerItem.startRead();
            HANDLER_ITEM_CONCURRENT_HASH_MAP.put(session.getId(), handlerItem);
        }
@ServerEndpoint("/ws/ssh")
@Component
public class SshHandler {
        private static final ConcurrentHashMap<String, HandlerItem> HANDLER_ITEM_CONCURRENT_HASH_MAP = new ConcurrentHashMap<>();

        @PostConstruct
        public void init() {
            System.out.println("websocket 加載");
        }
        private static Logger log = LoggerFactory.getLogger(SshHandler.class);
        private static final AtomicInteger OnlineCount = new AtomicInteger(0);
        // concurrent包的線程安全Set,用來存放每個客戶端對應(yīng)的Session對象。
        private static CopyOnWriteArraySet<javax.websocket.Session> SessionSet = new CopyOnWriteArraySet<javax.websocket.Session>();


        /**
         * 連接建立成功調(diào)用的方法
         */
        @OnOpen
        public void onOpen(javax.websocket.Session session) throws Exception {
            SessionSet.add(session);
            SshModel sshItem = new SshModel();
            sshItem.setHost("xxxx");
            sshItem.setPort(xxxx);
            sshItem.setUser("xxxx");
            sshItem.setPassword("xxxxx");
            int cnt = OnlineCount.incrementAndGet(); // 在線數(shù)加1
            log.info("有連接加入,當(dāng)前連接數(shù)為:{},sessionId={}", cnt,session.getId());
            SendMessage(session, "連接成功,sessionId="+session.getId());
            HandlerItem handlerItem = new HandlerItem(session, sshItem);
            handlerItem.startRead();
            HANDLER_ITEM_CONCURRENT_HASH_MAP.put(session.getId(), handlerItem);
        }

        /**
         * 連接關(guān)閉調(diào)用的方法
         */
        @OnClose
        public void onClose(javax.websocket.Session session) {
            SessionSet.remove(session);
            int cnt = OnlineCount.decrementAndGet();
            log.info("有連接關(guān)閉,當(dāng)前連接數(shù)為:{}", cnt);
        }

        /**
         * 收到客戶端消息后調(diào)用的方法
         * @param message
         * 客戶端發(fā)送過來的消息
         */
        @OnMessage
        public void onMessage(String message, javax.websocket.Session session) throws Exception {
            log.info("來自客戶端的消息:{}",message);
            //        SendMessage(session, "收到消息,消息內(nèi)容:"+message);
            HandlerItem handlerItem = HANDLER_ITEM_CONCURRENT_HASH_MAP.get(session.getId());
            this.sendCommand(handlerItem, message);
        }

        /**
         * 出現(xiàn)錯誤
         * @param session
         * @param error
         */
        @OnError
        public void onError(javax.websocket.Session session, Throwable error) {
            log.error("發(fā)生錯誤:{},Session ID: {}",error.getMessage(),session.getId());
            error.printStackTrace();
        }

        private void sendCommand(HandlerItem handlerItem, String data) throws Exception {
            if (handlerItem.checkInput(data)) {
                handlerItem.outputStream.write(data.getBytes());
            } else {
                handlerItem.outputStream.write("沒有執(zhí)行相關(guān)命令權(quán)限".getBytes());
                handlerItem.outputStream.flush();
                handlerItem.outputStream.write(new byte[]{3});
            }
            handlerItem.outputStream.flush();
        }

        /**
         * 發(fā)送消息,實踐表明,每次瀏覽器刷新,session會發(fā)生變化。
         * @param session
         * @param message
         */
        public static void SendMessage(javax.websocket.Session session, String message) {
            try {
                //            session.getBasicRemote().sendText(String.format("%s (From Server,Session ID=%s)",message,session.getId()));
                session.getBasicRemote().sendText(message);
                session.getBasicRemote().sendText("dhhw>$");
            } catch (IOException e) {
                log.error("發(fā)送消息出錯:{}", e.getMessage());
                e.printStackTrace();
            }
        }

        private class HandlerItem implements Runnable {
            private final javax.websocket.Session session;
            private final InputStream inputStream;
            private final OutputStream outputStream;
            private final Session openSession;
            private final ChannelShell channel;
            private final SshModel sshItem;
            private final StringBuilder nowLineInput = new StringBuilder();

            HandlerItem(javax.websocket.Session session, SshModel sshItem) throws IOException {
                this.session = session;
                this.sshItem = sshItem;
                this.openSession = JschUtil.openSession(sshItem.getHost(), sshItem.getPort(), sshItem.getUser(), sshItem.getPassword());
                this.channel = (ChannelShell) JschUtil.createChannel(openSession, ChannelType.SHELL);
                this.inputStream = channel.getInputStream();
                this.outputStream = channel.getOutputStream();
            }

            void startRead() throws JSchException {
                this.channel.connect();
                ThreadUtil.execute(this);
            }


            /**
             * 添加到命令隊列
             *
             * @param msg 輸入
             * @return 當(dāng)前待確認(rèn)待所有命令
             */
            private String append(String msg) {
                char[] x = msg.toCharArray();
                if (x.length == 1 && x[0] == 127) {
                    // 退格鍵
                    int length = nowLineInput.length();
                    if (length > 0) {
                        nowLineInput.delete(length - 1, length);
                    }
                } else {
                    nowLineInput.append(msg);
                }
                return nowLineInput.toString();
            }

            public boolean checkInput(String msg) {
                String allCommand = this.append(msg);
                boolean refuse;
                if (StrUtil.equalsAny(msg, StrUtil.CR, StrUtil.TAB)) {
                    String join = nowLineInput.toString();
                    if (StrUtil.equals(msg, StrUtil.CR)) {
                        nowLineInput.setLength(0);
                    }
                    refuse = SshModel.checkInputItem(sshItem, join);
                } else {
                    // 復(fù)制輸出
                    refuse = SshModel.checkInputItem(sshItem, msg);
                }
                return refuse;
            }


            @Override
            public void run() {
                try {
                    byte[] buffer = new byte[1024];
                    int i;
                    //如果沒有數(shù)據(jù)來,線程會一直阻塞在這個地方等待數(shù)據(jù)。
                    while ((i = inputStream.read(buffer)) != -1) {
                        sendBinary(session, new String(Arrays.copyOfRange(buffer, 0, i), sshItem.getCharsetT()));
                    }
                } catch (Exception e) {
                    if (!this.openSession.isConnected()) {
                        return;
                    }

                    SshHandler.this.destroy(this.session);
                }
            }
        }

        public void destroy(javax.websocket.Session session) {
            HandlerItem handlerItem = HANDLER_ITEM_CONCURRENT_HASH_MAP.get(session.getId());
            if (handlerItem != null) {
                IoUtil.close(handlerItem.inputStream);
                IoUtil.close(handlerItem.outputStream);
                JschUtil.close(handlerItem.channel);
                JschUtil.close(handlerItem.openSession);
            }
            IoUtil.close(session);
            HANDLER_ITEM_CONCURRENT_HASH_MAP.remove(session.getId());
        }

        private static void sendBinary(javax.websocket.Session session, String msg) {
            //		if (!session.isOpen()) {
            //			// 會話關(guān)閉不能發(fā)送消息
            //			return;
            //		}

            //		synchronized (session.getId()) {
            //			BinaryMessage byteBuffer = new BinaryMessage(msg.getBytes());
            try {
              //  System.out.println("#####:"+msg);
                session.getBasicRemote().sendText(msg);
            } catch (IOException e) {
            }
            //		}
        }
    }

3.3 StringUtil工具類

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import cn.hutool.system.SystemUtil;
import com.alibaba.fastjson.JSON;

import java.io.File;
import java.util.List;

/**
 * @Description 方法運行參數(shù)工具
 *
 */
public class StringUtil {

    /**
     * 支持的壓縮包格式
     */
    public static final String[] PACKAGE_EXT = new String[]{"tar.bz2", "tar.gz", "tar", "bz2", "zip", "gz"};

    /**
     * 獲取啟動參數(shù)
     * @param args 所有參數(shù)
     * @param name 參數(shù)名
     * @return 值
     */
    public static String getArgsValue(String[] args, String name) {
        if (args == null) {
            return null;
        }
        for (String item : args) {
            item = StrUtil.trim(item);
            if (item.startsWith("--" + name + "=")) {
                return item.substring(name.length() + 3);
            }
        }
        return null;
    }

    /**
     * id輸入規(guī)則
     *
     * @param value 值
     * @param min   最短
     * @param max   最長
     * @return true
     */
    public static boolean isGeneral(CharSequence value, int min, int max) {
        String reg = "^[a-zA-Z0-9_-]{" + min + StrUtil.COMMA + max + "}$";
        return Validator.isMatchRegex(reg, value);
    }

    /**
     * 刪除文件開始的路徑
     *
     * @param file      要刪除的文件
     * @param startPath 開始的路徑
     * @param inName    是否返回文件名
     * @return /test/a.txt /test/  a.txt
     */
    public static String delStartPath(File file, String startPath, boolean inName) {
        String newWhitePath;
        if (inName) {
            newWhitePath = FileUtil.getAbsolutePath(file.getAbsolutePath());
        } else {
            newWhitePath = FileUtil.getAbsolutePath(file.getParentFile());
        }
        String itemAbsPath = FileUtil.getAbsolutePath(new File(startPath));
        itemAbsPath = FileUtil.normalize(itemAbsPath);
        newWhitePath = FileUtil.normalize(newWhitePath);
        String path = StrUtil.removePrefix(newWhitePath, itemAbsPath);
        //newWhitePath.substring(newWhitePath.indexOf(itemAbsPath) + itemAbsPath.length());
        path = FileUtil.normalize(path);
        if (path.startsWith(StrUtil.SLASH)) {
            path = path.substring(1);
        }
        return path;
    }

    /**
     * 獲取jdk 中的tools jar文件路徑
     *
     * @return file
     */
    public static File getToolsJar() {
        File file = new File(SystemUtil.getJavaRuntimeInfo().getHomeDir());
        return new File(file.getParentFile(), "lib/tools.jar");
    }

    /**
     * 指定時間的下一個刻度
     *
     * @return String
     */
    public static String getNextScaleTime(String time, Long millis) {
        DateTime dateTime = DateUtil.parse(time);
        if (millis == null) {
            millis = 30 * 1000L;
        }
        DateTime newTime = dateTime.offsetNew(DateField.SECOND, (int) (millis / 1000));
        return DateUtil.formatTime(newTime);
    }

//	/**
//	 * 刪除 yml 文件內(nèi)容注釋
//	 *
//	 * @param content 配置內(nèi)容
//	 * @return 移除后的內(nèi)容
//	 */
//	public static String deleteComment(String content) {
//		List<String> split = StrUtil.split(content, StrUtil.LF);
//		split = split.stream().filter(s -> {
//			if (StrUtil.isEmpty(s)) {
//				return false;
//			}
//			s = StrUtil.trim(s);
//			return !StrUtil.startWith(s, "#");
//		}).collect(Collectors.toList());
//		return CollUtil.join(split, StrUtil.LF);
//	}

    /**
     * json 字符串轉(zhuǎn) bean,兼容普通json和字符串包裹情況
     *
     * @param jsonStr json 字符串
     * @param cls     要轉(zhuǎn)為bean的類
     * @param <T>     泛型
     * @return data
     */
    public static <T> T jsonConvert(String jsonStr, Class<T> cls) {
        if (StrUtil.isEmpty(jsonStr)) {
            return null;
        }
        try {
            return JSON.parseObject(jsonStr, cls);
        } catch (Exception e) {
            return JSON.parseObject(JSON.parse(jsonStr).toString(), cls);
        }
    }

    /**
     * json 字符串轉(zhuǎn) bean,兼容普通json和字符串包裹情況
     *
     * @param jsonStr json 字符串
     * @param cls     要轉(zhuǎn)為bean的類
     * @param <T>     泛型
     * @return data
     */
    public static <T> List<T> jsonConvertArray(String jsonStr, Class<T> cls) {
        try {
            if (StrUtil.isEmpty(jsonStr)) {
                return null;
            }
            return JSON.parseArray(jsonStr, cls);
        } catch (Exception e) {
            Object parse = JSON.parse(jsonStr);
            return JSON.parseArray(parse.toString(), cls);
        }
    }
}

以上就是終端的實現(xiàn)了,(有問題可以私信我)希望能幫助到大家。文章來源地址http://www.zghlxwxcb.cn/news/detail-409381.html

到了這里,關(guān)于WebSocket+xterm+springboot+vue 實現(xiàn) xshell 操作linux終端功能的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • vue + xtermjs + websocket 前端網(wǎng)頁版終端,操作后端的docker容器

    vue + xtermjs + websocket 前端網(wǎng)頁版終端,操作后端的docker容器

    簡介: 1.自動版,無需維護(hù)websocket,由xterm-addon-attach插件監(jiān)控輸入輸出(推薦,簡單好用) 2.自定義版,維護(hù)websocket,自己實現(xiàn)輸入輸出 3.xtermjs、xterm-addon-attach、xterm-addon-fit、docker 4.以上版本都是由cdn形式html頁面實現(xiàn),如需vue-cli工程化實現(xiàn),只需import引入fitAddon 、attachAdd

    2024年02月12日
    瀏覽(15)
  • WebSocket+Vue+SpringBoot實現(xiàn)語音通話

    WebSocket+Vue+SpringBoot實現(xiàn)語音通話

    參考文章 整體思路 前端點擊開始對話按鈕后,將監(jiān)聽麥克風(fēng),獲取到當(dāng)前的音頻,將其裝化為二進(jìn)制數(shù)據(jù),通過websocket發(fā)送到webscoket服務(wù)端,服務(wù)端在接收后,將消息寫入給指定客戶端,客戶端拿到發(fā)送過來的二進(jìn)制音頻后再轉(zhuǎn)化播放 注意事項 由于音頻轉(zhuǎn)化后的二進(jìn)制數(shù)據(jù)

    2024年02月06日
    瀏覽(17)
  • SpringBoot+Vue整合WebSocket實現(xiàn)實時通訊

    SpringBoot+Vue整合WebSocket實現(xiàn)實時通訊

    ????????在開發(fā)過程中,我們經(jīng)常遇到需要對前臺的列表數(shù)據(jù),實現(xiàn)實時展示最新的幾條數(shù)據(jù),或者是調(diào)度的任務(wù)進(jìn)度條實現(xiàn)實時的刷新......,而對于這種需求,無狀態(tài)的http協(xié)議顯然無法滿足我們的需求,于是websocket協(xié)議應(yīng)運而生。websocket協(xié)議本質(zhì)上是一個基于tcp的協(xié)議

    2024年02月13日
    瀏覽(24)
  • SpringBoot+Vue 整合websocket實現(xiàn)簡單聊天窗口

    SpringBoot+Vue 整合websocket實現(xiàn)簡單聊天窗口

    1 輸入臨時名字充當(dāng)賬號使用 2 進(jìn)入聊天窗口 3 發(fā)送消息 (復(fù)制一個頁面,輸入其他名字,方便展示效果) 4 其他窗口效果 pom依賴 WebSocketConfig.java WebSocketServer.java MessageVo.java App.vue

    2024年02月09日
    瀏覽(25)
  • Vue3+springboot通過websocket實現(xiàn)實時通信

    Vue3+springboot通過websocket實現(xiàn)實時通信

    本文章使用vue3+springboot通過websocket實現(xiàn)兩個用戶之間的實時通信,聊天信息使用mongodb非關(guān)系型數(shù)據(jù)庫進(jìn)行存儲。 效果圖如下: 用戶發(fā)送信息 ?農(nóng)戶收到信息并發(fā)送回去 后臺消息打印 引入依賴 ? 配置 在config目錄下, 創(chuàng)建WebSocketConfig類 創(chuàng)建一個WebSocketServer來 處理連接 用戶

    2024年02月05日
    瀏覽(23)
  • vue+springboot+websocket實現(xiàn)消息通知,含應(yīng)用場景

    vue+springboot+websocket實現(xiàn)消息通知,含應(yīng)用場景

    vue、springboot 實現(xiàn)場景 點擊同步之后更新數(shù)據(jù),更新時間比較長,因此使用異步,之后該按鈕置灰,在數(shù)據(jù)更新完成之后,服務(wù)端通知客戶端已經(jīng)同步成功,通知提示框,用戶即可查看數(shù)據(jù) 前端 1、在對應(yīng)的頁面編寫初始化、連接成功,錯誤,接受信息方法 2、mounted或者cre

    2024年02月11日
    瀏覽(42)
  • SpringBoot+Netty+Vue+Websocket實現(xiàn)在線推送/聊天系統(tǒng)

    SpringBoot+Netty+Vue+Websocket實現(xiàn)在線推送/聊天系統(tǒng)

    ok,那么今天的話也是帶來這個非常常用的一個技術(shù),那就是咱們完成nutty的一個應(yīng)用,今天的話,我會介紹地很詳細(xì),這樣的話,拿到這個博文的代碼就基本上可以按照自己的想法去構(gòu)建自己的一個在線應(yīng)用了。比如聊天,在線消息推送之類的。其實一開始我原來的想法做在

    2024年02月03日
    瀏覽(27)
  • SpringBoot實現(xiàn)WebSocket發(fā)送接收消息 + Vue實現(xiàn)SocketJs接收發(fā)送消息

    1、https://www.mchweb.net/index.php/dev/887.html 2、https://itonline.blog.csdn.net/article/details/81221103?spm=1001.2101.3001.6661.1utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~default-1-81221103-blog-121078449.pc_relevant_aadepth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~default-1-81221103-blog-12107

    2024年02月05日
    瀏覽(20)
  • SpringBoot+WebSocket+VUE實現(xiàn)一個簡單的聊天機器人

    SpringBoot+WebSocket+VUE實現(xiàn)一個簡單的聊天機器人

    要實現(xiàn)一個簡單的聊天機器人,可以使用Spring Boot框架作為后端,使用WebSocket協(xié)議實現(xiàn)實時通信,使用VUE作為前端實現(xiàn)聊天界面。 引入jar包 在Spring Boot的配置類中添加WebSocket配置 這里的WebSocketHandler是自定義的WebSocket處理器,用于處理WebSocket消息。 做了一個簡單的攔截器,實

    2023年04月19日
    瀏覽(30)
  • web網(wǎng)頁端使用webSocket實現(xiàn)語音通話功能(SpringBoot+VUE)

    web網(wǎng)頁端使用webSocket實現(xiàn)語音通話功能(SpringBoot+VUE)

    最近在寫一個web項目,需要實現(xiàn)web客戶端之間的語音通話,期望能夠借助webSocket全雙工通信的方式來實現(xiàn),但是網(wǎng)上沒有發(fā)現(xiàn)可以正確使用的代碼。網(wǎng)上能找到的一個代碼使用之后 只能聽到“嘀嘀嘀”的雜音 解決方案: 使用Json來傳遞數(shù)據(jù)代替原有的二進(jìn)制輸入輸出流 技術(shù)

    2024年02月02日
    瀏覽(61)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包