🔨 添加 RDP 下载功能.

This commit is contained in:
lijiahangmax
2025-06-28 01:26:27 +08:00
parent 1328894188
commit da766f0b85
3 changed files with 170 additions and 7 deletions

View File

@@ -1,15 +1,15 @@
import type {
GuacdInitConfig,
GuacdReactiveSessionStatus,
IGuacdChannel,
IRdpSession,
IRdpSessionClipboardHandler,
TerminalSessionTabItem,
GuacdInitConfig,
IRdpSessionDisplayHandler,
TerminalSessionTabItem
GuacdReactiveSessionStatus,
IRdpSessionClipboardHandler
} from '@/views/terminal/interfaces';
import type { OutputPayload } 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 { useTerminalStore } from '@/store';
import Guacamole from 'guacamole-common-js';
@@ -25,6 +25,8 @@ export const CONNECT_TIMEOUT = 10000;
// RDP 会话实现
export default class RdpSession extends BaseSession<GuacdReactiveSessionStatus, IGuacdChannel> implements IRdpSession {
public fileSystemName: string;
public config: GuacdInitConfig;
public client: Guacamole.Client;
@@ -40,6 +42,7 @@ export default class RdpSession extends BaseSession<GuacdReactiveSessionStatus,
closeCode: 0,
closeMessage: ''
});
this.fileSystemName = 'Shared Driver';
this.client = undefined as unknown as Guacamole.Client;
this.config = {} as unknown as GuacdInitConfig;
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);
// 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;
}
// 设置为已关闭
setClosed() {
// 设置为已关闭
super.setClosed();
// 关闭文件传输
useTerminalStore().transferManager.rdp.closeBySessionKey(this.sessionKey);
}
// 断开连接
disconnect(): void {
super.disconnect();

View File

@@ -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;
}
}

View File

@@ -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();
}
}