vue + xtermjs + websocket 前端网页版终端,操作后端的docker容器

本文介绍了两种方式实现前端通过WebSocket与Docker容器进行交互的WebSSH终端。自动版利用xterm-addon-attach插件简化流程,自定义版则需要手动处理WebSocket的输入输出。两种实现均基于xtermjs库,支持Vue集成,并提供窗口大小自适应功能。
摘要由CSDN通过智能技术生成
简介:
1.自动版,无需维护websocket,由xterm-addon-attach插件监控输入输出(推荐,简单好用)
2.自定义版,维护websocket,自己实现输入输出
3.xtermjsxterm-addon-attachxterm-addon-fitdocker
4.以上版本都是由cdn形式html页面实现,如需vue-cli工程化实现,只需import引入fitAddon 、attachAddon 替换即可

一、自动版(推荐)
如图所示:

在这里插入图片描述

代码(复制另存txt,修改.html直接运行)
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>前端终端,操作后端的docker容器</title>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/xterm@4.18.0/css/xterm.css">
</head>


<body class="bodyCss">
    <div id="app">
        <div id="terminal" ></div>
    </div>
</body>
<script src="https://unpkg.com/xterm@4.18.0/lib/xterm.js"></script>
<script src="https://lib.baomitu.com/vue/2.6.14/vue.js"></script>
<script  src="https://unpkg.com/xterm-addon-fit@0.6.0/lib/xterm-addon-fit.js"></script>
<script  src="https://unpkg.com/xterm-addon-attach@0.6.0/lib/xterm-addon-attach.js"></script>

<script>
    let wsTime = null
    new Vue({
        el: '#app',
        data() {
            return {
                // 终端
                term:{},
                // websocket
                ws:{}
            }
        },
        created(){
            // 初始化终端
            this.initTerminal() 
        },
        mounted() {
            // 建立websocket连接
            this.websocket()
        },
        beforeDestroy() {
            this.ws.close()
            this.term.dispose()
        },
        methods: {
            // 初始化终端配置
            initTerminal(){
                this.term = new Terminal({
                    rendererType: "canvas", //渲染类型
                    // rows: 40, //行数,影响最小高度
                    // cols: 100, // 列数,影响最小宽度
                    convertEol: true, //启用时,光标将设置为下一行的开头
                    // scrollback: 50, //终端中的滚动条回滚量
                    disableStdin: false, //是否应禁用输入。
                    cursorStyle: "underline", //光标样式
                    cursorBlink: true, //光标闪烁
                    theme: {
                        foreground: '#F8F8F8',
                        background: '#2D2E2C',
                        cursor: "help", //设置光标
                        lineHeight: 16,
                    },
                    fontFamily: '"Cascadia Code", Menlo, monospace'
                });
            },
            // 自定义终端默认展示内容
            writeDefaultInfo(){
                let defaultInfo = [
                '┌\x1b[1m terminals \x1b[0m─────────────────────────────────────────────────────────────────┐ ',
                '│                                                                            │ ',
                '│  \x1b[1;34m 欢迎使用Web Docker SSH \x1b[0m                                                  │ ',
                '│                                                                            │ ',
                '└────────────────────────────────────────────────────────────────────────────┘ ',]
                this.term.write(defaultInfo.join('\n\r'))
            },
            // 建立websocket连接
            websocket() {
                // WebSocket start
                if ('WebSocket' in window) {
                	//需要修改ip和id
                	//例如:const url = `ws://192.168.111.222:2375/v1.41/containers/0eb8aafb4e6e/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1`
                    const url = `ws://你的ip/v1.41/containers/你的id/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1`
                    const ws = new WebSocket(url)
                    this.ws = ws
                    ws.onopen = (event) => {
                        console.log('已建立连接:',event)
                        // 输入换行符可让终端显示当前用户的工作路径
                        ws.send('\n') 
                        // 窗口自适应插件
                        const fitAddon = new FitAddon.FitAddon();
                        // websocket自动收发消息插件
                        const attachAddon = new AttachAddon.AttachAddon(ws)
                        this.term.loadAddon(attachAddon)
                        this.term.loadAddon(fitAddon)
                        this.term.open(document.getElementById('terminal'));
                        // 聚焦闪烁光标
                        this.term.focus()
                        // 窗口尺寸变化时,终端尺寸自适应
                        window.onresize = () => { 
                            fitAddon.fit()
                        }
                        // 自定义终端默认展示内容
                        this.writeDefaultInfo()
                    };
                    ws.onmessage = (event) => {
                        console.log('接收信息:',event)
                    };
                    ws.onerror = (event) => {
                        console.log('错误信息:', event)
                        if (wsTime) {
                            window.clearTimeout(wsTime)
                            wsTime = null
                        }
                        wsTime = window.setTimeout(() => {
                            this.websocket()
                        }, 3000)
                    };
                    ws.onclose = (event) => {
                        console.log('已关闭连接:', event)
                        // if (wsTime) {
                        //    window.clearTimeout(wsTime)
                        //    wsTime = null
                        // }
                        // wsTime = window.setTimeout(() => {
                        //    this.websocket()
                        // }, 3000)
                    };
                } else {
                    console.log('浏览器不支持 WebSocket..')
                }
                // WebSocket end
            }
        }
    })
</script>

</html>



二、自定义版
如图所示:

在这里插入图片描述

代码(复制另存txt,修改.html直接运行)
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>前端终端,操作后端的docker容器</title>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/xterm@4.18.0/css/xterm.css">
</head>

<body class="bodyCss">
    <div id="app">
        <div id="terminal" ></div>
    </div>
</body>
<script src="https://unpkg.com/xterm@4.18.0/lib/xterm.js"></script>
<script src="https://lib.baomitu.com/vue/2.6.14/vue.js"></script>
<script  src="https://unpkg.com/xterm-addon-fit@0.6.0/lib/xterm-addon-fit.js"></script>

<script>
    let wsTime = null
    new Vue({
        el: '#app',
        data() {
            return {
                // 终端
                term:{},
                // websocket
                ws:{},
                // 用户输入
                command:''
            }
        },
        created(){
            // 初始化终端
            this.initTerminal() 
        },
        mounted() {
            // 建立websocket连接
            this.websocket()
        },
        beforeDestroy() {
            this.ws.close()
            this.term.dispose()
        },
        methods: {
            // 初始化终端配置
            initTerminal(){
                this.term = new Terminal({
                    rendererType: "canvas", //渲染类型
                    // rows: 40, //行数,影响最小高度
                    // cols: 100, // 列数,影响最小宽度
                    convertEol: true, //启用时,光标将设置为下一行的开头
                    // scrollback: 50, //终端中的滚动条回滚量
                    disableStdin: false, //是否应禁用输入。
                    cursorStyle: "underline", //光标样式
                    cursorBlink: true, //光标闪烁
                    theme: {
                        foreground: '#F8F8F8',
                        background: '#2D2E2C',
                        cursor: "help", //设置光标
                        lineHeight: 16,
                    },
                    fontFamily: '"Cascadia Code", Menlo, monospace'
                });
            },
            // 自定义终端默认展示内容
            writeDefaultInfo(){
                let defaultInfo = [
                '┌\x1b[1m terminals \x1b[0m─────────────────────────────────────────────────────────────────┐ ',
                '│                                                                            │ ',
                '│  \x1b[1;34m 欢迎使用Web Docker SSH \x1b[0m                                                  │ ',
                '│                                                                            │ ',
                '└────────────────────────────────────────────────────────────────────────────┘ ',]
                // 测试颜色区间 
                // let arr = Array.from({length:100},(v,i)=>v = i)
                // console.log(arr)
                // arr.map((item,i) => {
                //     defaultInfo.push(`Hello from \x1B[1;3;${i}m ${i} \x1B[0m  \u2764\ufe0f   ${i}`)
                // })
                this.term.write(defaultInfo.join('\n\r'))
                this.writeOfColor('我是加粗斜体红色的字呀','1;3;','31m')
                // this.term.write('\n\r$ ')
            },
            // 
            writeOfColor(txt, fontCss = "", bgColor = ""){
                // 在Linux脚本中以 \x1B[ 开始,中间前部分是样式+内容,以 \x1B[0m 结尾
                // 示例 \x1B[1;3;31m 内容 \x1B[0m  
                // fontCss
                // 0;-4;字体样式(0;正常 1;加粗 2;变细 3;斜体 4;下划线)
                // bgColor
                // 30m-37m字体颜色(30m:黑色 31m:红色 32m:绿色 33m:棕色字 34m:蓝色 35m:洋红色/紫色 36m:蓝绿色/浅蓝色 37m:白色)
                // 40m-47m背景颜色(40m:黑色 41m:红色 42m:绿色 43m:棕色字 44m:蓝色 45m:洋红色/紫色 46m:蓝绿色/浅蓝色 47m:白色)
                this.term.write(`\x1B[${fontCss}${bgColor}${txt}\x1B[0m`)
            },
            // 监听输入
            userWrite(){
                this.term.onData(e => {
                    switch (e) {
                        case '\u0003': // Ctrl+C
                            this.term.write('^C ');
                            this.term.write('\r\n$ ')
                        break;
                        case '\r': // Enter
                            this.ws.send(this.command)
                            this.ws.send('\n') 
                            this.command = ''
                            // this.term.write('\r\n$ ')
                        break;
                        case '\u007F': // Backspace (DEL)
                            // Do not delete the prompt
                            if (this.term._core.buffer.x > 2) {
                                this.term.write('\b \b');
                                if (this.command.length > 0) {
                                    this.command = this.command.substr(0, this.command.length - 1);
                                }
                            }
                        break;
                        default: // Print all other characters for demo
                            if (e >= String.fromCharCode(0x20) && e <= String.fromCharCode(0x7E) || e >= '\u00a0') {
                                this.command += e;
                                this.writeOfColor(e,'2;3;','33m')
                                console.log('用户输入command',this.command)
                            }
                    }
                });
            },
            // 建立websocket连接
            websocket() {
                // WebSocket start
                if ('WebSocket' in window) {
                    //需要修改ip和id
                	//例如:const url = `ws://192.168.111.222:2375/v1.41/containers/0eb8aafb4e6e/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1`
                    const url = `ws://你的ip/v1.41/containers/你的id/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1`
                    const ws = new WebSocket(url)
                    this.ws = ws
                    this.$nextTick(()=>{
                        this.userWrite()
                    })
                    ws.onopen = (event) => {
                        console.log('已建立连接:',event)
                        // 输入换行符可让终端显示当前用户的工作路径
                        ws.send('\n') 
                        // 窗口自适应插件
                        const fitAddon = new FitAddon.FitAddon();
                        // 窗口尺寸变化时,终端尺寸自适应
                        window.onresize = () => { 
                            fitAddon.fit()
                        }
                        this.term.loadAddon(fitAddon)
                        this.term.open(document.getElementById('terminal'));
                        this.term.focus()
                        // 自定义终端默认展示内容
                        this.writeDefaultInfo()
                    };
                    ws.onmessage = (event) => {
                        console.log('接收信息:',event)
                        //将字符串转换成 Blob对象
                        const blob = new Blob([event.data], {
                            type: 'text/plain'
                        });
                        //将Blob 对象转换成字符串
                        const reader = new FileReader();
                        reader.readAsText(blob, 'utf-8');
                        reader.onload = (e) => {
                            // 可以根据返回值判断使用何种颜色或者字体,不过返回值自带了一些字体颜色
                            this.writeOfColor(reader.result,'0;','37m')
                        }
                    };
                    ws.onerror = (event) => {
                        console.log('错误信息:', event)
                        if (wsTime) {
                            window.clearTimeout(wsTime)
                            wsTime = null
                        }
                        wsTime = window.setTimeout(() => {
                            this.websocket()
                        }, 3000)
                    };
                    ws.onclose = (event) => {
                        console.log('已关闭连接:', event)
                        // if (wsTime) {
                        //    window.clearTimeout(wsTime)
                        //    wsTime = null
                        // }
                        // wsTime = window.setTimeout(() => {
                        //    this.websocket()
                        // }, 3000)
                    };
                } else {
                    console.log('浏览器不支持 WebSocket..')
                }
                // WebSocket end
            }
        }
    })
</script>

</html>
要在 Vue 中使用 xterm.js 和 WebSocket 实现终端,你需要将用户输入的命令发送给后端,然后将后端返回的结果输出到 xterm.js 终端中。以下是一个简单的示例: ```html <template> <div id="terminal"></div> </template> <script> import { Terminal } from 'xterm'; import { FitAddon } from 'xterm-addon-fit'; export default { data() { return { socket: null, // WebSocket 实例 term: null, // Terminal 实例 }; }, mounted() { // 创建 WebSocket 实例 this.socket = new WebSocket('ws://localhost:8080'); // 创建 Terminal 实例 this.term = new Terminal(); const fitAddon = new FitAddon(); this.term.loadAddon(fitAddon); this.term.open(document.getElementById('terminal')); // 处理 WebSocket 消息 this.socket.onmessage = (event) => { this.term.write(event.data); }; // 处理输入事件 this.term.onData(data => { this.socket.send(data); }); // 调整终端大小 this.term.onResize(size => { const cols = size.cols; const rows = size.rows; this.socket.send(JSON.stringify({ type: 'resize', cols, rows })); }); // 发送 resize 消息 const cols = this.term.cols; const rows = this.term.rows; this.socket.send(JSON.stringify({ type: 'resize', cols, rows })); }, beforeDestroy() { // 关闭 WebSocket 连接 this.socket.close(); } } </script> ``` 以上代码中,我们首先在 `mounted` 钩子函数中创建了一个 WebSocket 实例和一个 Terminal 实例。然后我们为 WebSocket 实例添加了一个 `onmessage` 事件监听器,该监听器会在接收到服务器返回的消息时触发,我们在该事件处理函数中将消息输出到终端中。 接着,我们为 Terminal 实例添加了一个 `onData` 事件监听器,该监听器会在用户输入时触发,我们在该事件处理函数中向服务器发送用户输入的命令。同时,我们还为 Terminal 实例添加了一个 `onResize` 事件监听器,该监听器会在终端大小调整时触发,我们在该事件处理函数中向服务器发送终端大小变化的消息。 最后,我们在 `beforeDestroy` 钩子函数中关闭了 WebSocket 连接。 需要注意的是,以上代码中的 WebSocket 连接是通过 `ws://localhost:8080` 连接本地服务器的,你需要根据实际情况修改 WebSocket 连接地址。另外,代码中的消息格式和处理逻辑也需要根据实际情况进行修改。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘斩仙的笔记本

富贵险中求

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值