前端Vue中使用Guacamole进行远程桌面的前端部分

西瓜味的桃子 2024-10-03 10:03:03 阅读 74

最近公司有个项目要求远程虚拟机,最后选择使用了Guacamole进行实现,网上搜了一下感觉资料不是很多,对guacamole-common.js里的API介绍也没查到(捂脸),可能是我的问题。网上的教程都是教后端怎么搭建远程桌面系统。对web端使用的资料很少,可能大佬们觉着不需要(╥╯^╰╥)。下面我把在项目中用到的一下方法,总结一下,万一有人也需要呐。哈哈

下载guacamole-common或者直接引入guacamole-common.js文件;在项目中的引用

<code>初始化Guacamole

const initGuacamole = () => { -- -->

//XXXXX 后端提供的连接地址

guacamole.tunnel = new Guacamole.WebSocketTunnel('XXXXX')

if (guacamole.client) {

guacamole.display.scale(0)

uninstallKeyboard()

}

guacamole.client = new Guacamole.Client(guacamole.tunnel)

guacamole.tunnel.onerror = error => {

// console.error(`Tunnel failed ${JSON.stringify(error)}`)

// 这里是断开重连的机智 当code值是下面这些时会进行重连5次,

if (error.code === 512 || error.code === 514 || error.code === 515 || error.code === 769 || error.code === 776) {

if (guacamole.curRetryCount === 0) {

guacamole.retryTask = setInterval(() => {

if (guacamole.curRetryCount < guacamole.maxRetryCount) {

uninstallKeyboard()

nextTick(() => {

initGuacamole('error')

})

guacamole.curRetryCount++

} else if (guacamole.curRetryCount >= guacamole.maxRetryCount) {

guacamole.loading = false

guacamole.loadingText = ''

uninstallKeyboard()

clearInterval(guacamole.retryTask)

guacamole.retryTask = null

}

}, 3000)

}

}else {

if (!guacamole.retryTask) {

guacamole.loading = false

guacamole.loadingText = ''

}

}

}

guacamole.tunnel.onstatechange = state => {

// console.log('tunnel.onstatechange',Guacamole.Tunnel.State,state);

switch (state) {

case Guacamole.Tunnel.State.CONNECTING:

guacamole.connectionState = TUNNEL_STATES.CONNECTING

break;

// Connection is established / no longer unstable

case Guacamole.Tunnel.State.OPEN:

guacamole.connectionState = TUNNEL_STATES.CONNECTED

break;

// Connection is established but misbehaving

case Guacamole.Tunnel.State.UNSTABLE:

// TODO

guacamole.connectionState = '不稳定'

// this.$message.error("不稳定")

break;

// Connection has closed

case Guacamole.Tunnel.State.CLOSED:

guacamole.connectionState = TUNNEL_STATES.DISCONNECTED

break;

}

}

guacamole.client.onstatechange = clientState => {

// console.log('clientState', clientState, TUNNEL_STATES);

switch (clientState) {

case 0:

guacamole.connectionState = TUNNEL_STATES.IDLE

break

case 1:

// connecting ignored for some reason?

break

case 2:

guacamole.connectionState = TUNNEL_STATES.WAITING

break

case 3:

guacamole.connectionState = TUNNEL_STATES.CONNECTED

// 取消加载

guacamole.loading = false

guacamole.loadingText = ''

// 重连清空定时器和次数

guacamole.curRetryCount = 0

clearInterval(guacamole.retryTask)

guacamole.retryTask = null

//监听视图变化,并发送最新的宽高给Guacamole进行重新绘制

window.addEventListener('resize', resize)

case 4:

case 5:

// disconnected, disconnecting

break

}

}

guacamole.client.onerror = error => {

guacamole.client.disconnect()

console.error(`Client error ${ JSON.stringify(error)}`)

}

guacamole.display = guacamole.client.getDisplay()

display.value.appendChild(guacamole.display.getElement())

display.value.addEventListener('contextmenu', e => {

e.stopPropagation();

if (e.preventDefault) {

e.preventDefault();

}

e.returnValue = false;

})

// 后端需要的参数

let param = {

userName: '',

password: '',

type: 'VNC/RDP'

}

guacamole.client.connect(param);

// 视图大小发生变化时触发(rdp和VNC同时连接时rdp可以动态设置分辨率,但是VNC不可以,所以前端可以通过onresize拿到rdp连接后,VNC通道拿到流的宽高进行等比缩放)

guacamole.display.onresize = function(width, height) {

const resizeScale = Math.min(

viewport.value.clientWidth / Math.max(guacamole.display.getWidth(), 1),

viewport.value.clientHeight / Math.max(guacamole.display.getHeight(), 1)

)

guacamole.display.scale(resizeScale)

}

window.onunload = () => guacamole.client.disconnect();

// 解决连接多个虚拟机时有的虚拟机不能查看z-index: -1

guacamole.client.getDisplay().scale(1)

display.value.onclick = () => {

display.value.focus()

}

display.value.onfocus = () => {

display.value.className = 'focus'

}

display.value.onblur = () => {

display.value.className = ''

}

guacamole.keyboard = new Guacamole.Keyboard(display.value);

installKeyboard()

// Mouse 参数最好使用guacamole.client.getDisplay().getElement(),之前写的是父级元素,会导致出现两个鼠标的问题

guacamole.mouse = new Guacamole.Mouse(guacamole.client.getDisplay().getElement());

// 鼠标离开显示器时隐藏软件光标

guacamole.mouse.onmouseout = () => {

if (!guacamole.display) return;

guacamole.display.showCursor(false);

}

guacamole.mouse.onmousedown = guacamole.mouse.onmouseup = guacamole.mouse.onmousemove = handleMouseState

setTimeout(() => {

resize()

display.value.focus()

}, 1000);

//居中显示(当返回的流宽高小于屏幕宽高时)

guacamole.client.getDisplay().getElement().style.setProperty('margin', 'auto')

}

缩放方法

const resize = () => {

const elm = viewport.value;

if (!elm || !elm.offsetWidth) {

return

}

// let pixelDensity = window.devicePixelRatio || 1 (pixelDensity 获取当前电脑的分辨率百分比)

let pixelDensity = 1

const width = elm.clientWidth * pixelDensity

const height = elm.clientHeight * pixelDensity

if (guacamole.display.getWidth() !== width || guacamole.display.getHeight() !== height) {

guacamole.client.sendSize(width, height)

}

// setting timeout so display has time to get the correct size

setTimeout(() => {

// 计算缩放比例

const scale = Math.min(

elm.clientWidth / Math.max(guacamole.display.getWidth(), 1),

elm.clientHeight / Math.max(guacamole.display.getHeight(), 1)

)

// guacamole.display.scale(1)

if (props.websocketUrl === '/webSocket') {

guacamole.display.scale(1)

}else {

guacamole.display.scale(scale)

}

}, 1000)

}

html

<div ref="display" class="display" tabindex="0"/>code>

以上就是我在项目中的使用情况

1、连接Tunnel

guacamole.tunnel = new Guacamole.WebSocketTunnel(props.websocketUrl)

2、把流放到视图中

guacamole.client = new Guacamole.Client(guacamole.tunnel)

3、监听tunnel和client 的值进行状态判断

4、把流添加到视图中

guacamole.client.getDisplay().appendChild(guacamole.display.getElement())

5、给后端传参

let param = {userName: ’ ',password: ’ '}

guacamole.client.connect(param);

6、断开虚拟机连接

guacamole.client.disconnect();

7、键盘事件

// display.value div元素

new Guacamole.Keyboard(display.value)

8、鼠标事件

new Guacamole.Mouse(guacamole.client.getDisplay().getElement());



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。