🔨 添加 RDP 下载功能.
This commit is contained in:
@@ -1,15 +1,15 @@
|
|||||||
import type {
|
import type {
|
||||||
GuacdInitConfig,
|
|
||||||
GuacdReactiveSessionStatus,
|
|
||||||
IGuacdChannel,
|
IGuacdChannel,
|
||||||
IRdpSession,
|
IRdpSession,
|
||||||
IRdpSessionClipboardHandler,
|
TerminalSessionTabItem,
|
||||||
|
GuacdInitConfig,
|
||||||
IRdpSessionDisplayHandler,
|
IRdpSessionDisplayHandler,
|
||||||
TerminalSessionTabItem
|
GuacdReactiveSessionStatus,
|
||||||
|
IRdpSessionClipboardHandler
|
||||||
} from '@/views/terminal/interfaces';
|
} from '@/views/terminal/interfaces';
|
||||||
import type { OutputPayload } from '@/views/terminal/types/protocol';
|
import type { OutputPayload } from '@/views/terminal/types/protocol';
|
||||||
import { InputProtocol } from '@/views/terminal/types/protocol';
|
import { InputProtocol } from '@/views/terminal/types/protocol';
|
||||||
import { fitDisplayValue, TerminalCloseCode, TerminalMessages } from '@/views/terminal/types/const';
|
import { TerminalMessages, fitDisplayValue, TerminalCloseCode } from '@/views/terminal/types/const';
|
||||||
import { screenshot } from '@/views/terminal/types/utils';
|
import { screenshot } from '@/views/terminal/types/utils';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
import Guacamole from 'guacamole-common-js';
|
import Guacamole from 'guacamole-common-js';
|
||||||
@@ -25,6 +25,8 @@ export const CONNECT_TIMEOUT = 10000;
|
|||||||
// RDP 会话实现
|
// RDP 会话实现
|
||||||
export default class RdpSession extends BaseSession<GuacdReactiveSessionStatus, IGuacdChannel> implements IRdpSession {
|
export default class RdpSession extends BaseSession<GuacdReactiveSessionStatus, IGuacdChannel> implements IRdpSession {
|
||||||
|
|
||||||
|
public fileSystemName: string;
|
||||||
|
|
||||||
public config: GuacdInitConfig;
|
public config: GuacdInitConfig;
|
||||||
|
|
||||||
public client: Guacamole.Client;
|
public client: Guacamole.Client;
|
||||||
@@ -40,6 +42,7 @@ export default class RdpSession extends BaseSession<GuacdReactiveSessionStatus,
|
|||||||
closeCode: 0,
|
closeCode: 0,
|
||||||
closeMessage: ''
|
closeMessage: ''
|
||||||
});
|
});
|
||||||
|
this.fileSystemName = 'Shared Driver';
|
||||||
this.client = undefined as unknown as Guacamole.Client;
|
this.client = undefined as unknown as Guacamole.Client;
|
||||||
this.config = {} as unknown as GuacdInitConfig;
|
this.config = {} as unknown as GuacdInitConfig;
|
||||||
this.displayHandler = undefined as unknown as IRdpSessionDisplayHandler;
|
this.displayHandler = undefined as unknown as IRdpSessionDisplayHandler;
|
||||||
@@ -94,8 +97,16 @@ export default class RdpSession extends BaseSession<GuacdReactiveSessionStatus,
|
|||||||
};
|
};
|
||||||
// 剪切板回调
|
// 剪切板回调
|
||||||
this.client.onclipboard = this.clipboardHandler.receiveRemoteClipboardData.bind(this);
|
this.client.onclipboard = this.clipboardHandler.receiveRemoteClipboardData.bind(this);
|
||||||
|
// 文件系统回调
|
||||||
// TODO 下载文件
|
this.client.onfilesystem = (_, fileSystemName) => {
|
||||||
|
if (fileSystemName) {
|
||||||
|
this.fileSystemName = fileSystemName;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 下载文件回调
|
||||||
|
this.client.onfile = (stream, mimetype, filename) => {
|
||||||
|
useTerminalStore().transferManager.rdp.addDownload(this, stream, mimetype, filename);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 连接会话
|
// 连接会话
|
||||||
@@ -209,6 +220,14 @@ export default class RdpSession extends BaseSession<GuacdReactiveSessionStatus,
|
|||||||
return this.status.connected && this.status.canWrite;
|
return this.status.connected && this.status.canWrite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置为已关闭
|
||||||
|
setClosed() {
|
||||||
|
// 设置为已关闭
|
||||||
|
super.setClosed();
|
||||||
|
// 关闭文件传输
|
||||||
|
useTerminalStore().transferManager.rdp.closeBySessionKey(this.sessionKey);
|
||||||
|
}
|
||||||
|
|
||||||
// 断开连接
|
// 断开连接
|
||||||
disconnect(): void {
|
disconnect(): void {
|
||||||
super.disconnect();
|
super.disconnect();
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
import type { IFileDownloadTask, FileTransferItem, IRdpSession } from '@/views/terminal/interfaces';
|
||||||
|
import { TransferType, TransferStatus, TerminalMessages, TransferSource } from '../../types/const';
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
import Guacamole from 'guacamole-common-js';
|
||||||
|
import BaseFileTransferTask from './base-file-transfer-task';
|
||||||
|
|
||||||
|
// rdp 下载任务实现
|
||||||
|
export default class RdpFileDownloadTask extends BaseFileTransferTask implements IFileDownloadTask {
|
||||||
|
|
||||||
|
private session: IRdpSession;
|
||||||
|
private stream: Guacamole.InputStream;
|
||||||
|
private reader: Guacamole.BlobReader;
|
||||||
|
|
||||||
|
constructor(session: IRdpSession, stream: Guacamole.InputStream, mimetype: string, fileItem: FileTransferItem) {
|
||||||
|
super(TransferType.DOWNLOAD, TransferSource.RDP, session.info.hostId, session.sessionKey, fileItem, {});
|
||||||
|
this.stream = stream;
|
||||||
|
this.session = session;
|
||||||
|
this.reader = new Guacamole.BlobReader(this.stream, mimetype);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始下载
|
||||||
|
start(): void {
|
||||||
|
this.onStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载完成
|
||||||
|
finish(): void {
|
||||||
|
this.onFinish();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载中断
|
||||||
|
abort(): void {
|
||||||
|
this.onAbort();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载失败
|
||||||
|
error(): void {
|
||||||
|
this.onError(undefined as unknown as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始回调
|
||||||
|
onStart(): void {
|
||||||
|
try {
|
||||||
|
this.state.status = TransferStatus.TRANSFERRING;
|
||||||
|
// 发送开始
|
||||||
|
this.stream.sendAck('OK', Guacamole.Status.Code.SUCCESS);
|
||||||
|
// 进度回调
|
||||||
|
this.reader.onprogress = (len) => this.onProgress(undefined, len);
|
||||||
|
// 结束回调
|
||||||
|
this.reader.onend = this.onFinish.bind(this);
|
||||||
|
} catch (e) {
|
||||||
|
this.onError(TerminalMessages.fileTransferError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 进度发生变化
|
||||||
|
onProgress(_: number | undefined, currentSize: number | undefined): void {
|
||||||
|
if (this.state.aborted || this.state.finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 设置进度
|
||||||
|
this.state.totalSize += currentSize || 0;
|
||||||
|
this.state.currentSize += currentSize || 0;
|
||||||
|
// 发送 ACK
|
||||||
|
if (this.stream) {
|
||||||
|
this.stream.sendAck('Received', Guacamole.Status.Code.SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 完成回调
|
||||||
|
onFinish(): void {
|
||||||
|
if (this.state.aborted || this.state.finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.state.finished = true;
|
||||||
|
this.state.progress = 100;
|
||||||
|
this.state.status = TransferStatus.SUCCESS;
|
||||||
|
// 完成下载文件
|
||||||
|
const blob = this.reader.getBlob();
|
||||||
|
saveAs(blob, this.fileItem.name);
|
||||||
|
// 释放资源
|
||||||
|
this.releaseResource();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载终端回调
|
||||||
|
onAbort(): void {
|
||||||
|
if (this.state.aborted || this.state.finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.state.aborted = true;
|
||||||
|
try {
|
||||||
|
if (this.session.status.connected) {
|
||||||
|
if (this.stream) {
|
||||||
|
// 发送 ACK
|
||||||
|
this.stream.sendAck('Aborted', Guacamole.Status.Code.RESOURCE_CLOSED);
|
||||||
|
// 关闭流
|
||||||
|
this.session.client.endStream(this.stream.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 触发失败
|
||||||
|
this.onError(TerminalMessages.sessionClosed);
|
||||||
|
} catch (e) {
|
||||||
|
// 触发失败
|
||||||
|
this.onError(TerminalMessages.fileTransferError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载失败回调
|
||||||
|
onError(msg: string | undefined): void {
|
||||||
|
this.state.finished = true;
|
||||||
|
this.state.status = TransferStatus.ERROR;
|
||||||
|
this.state.errorMessage = msg || TerminalMessages.fileTransferError;
|
||||||
|
// 释放资源
|
||||||
|
this.releaseResource();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 释放资源
|
||||||
|
private releaseResource() {
|
||||||
|
if (this.stream) {
|
||||||
|
this.stream.onend = null;
|
||||||
|
this.stream.onblob = null;
|
||||||
|
}
|
||||||
|
this.stream = undefined as unknown as Guacamole.InputStream;
|
||||||
|
this.reader = undefined as unknown as Guacamole.BlobReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import type { ITerminalTransferManager, IRdpTransferManager, ISftpTransferManager } from '@/views/terminal/interfaces';
|
||||||
|
import SftpTransferManager from './sftp-transfer-manager';
|
||||||
|
import RdpTransferManager from './rdp-transfer-manager';
|
||||||
|
|
||||||
|
// 传输管理器基类
|
||||||
|
export default class TerminalTransferManager implements ITerminalTransferManager {
|
||||||
|
|
||||||
|
public sftp: ISftpTransferManager;
|
||||||
|
|
||||||
|
public rdp: IRdpTransferManager;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.sftp = new SftpTransferManager();
|
||||||
|
this.rdp = new RdpTransferManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user