🔨 添加 RDP 上传功能.
This commit is contained in:
@@ -34,7 +34,7 @@ import { TerminalSessionTypes, TerminalTabs } from '@/views/terminal/types/const
|
|||||||
import TerminalTabManager from '@/views/terminal/service/tab/terminal-tab-manager';
|
import TerminalTabManager from '@/views/terminal/service/tab/terminal-tab-manager';
|
||||||
import TerminalPanelManager from '@/views/terminal/service/tab/terminal-panel-manager';
|
import TerminalPanelManager from '@/views/terminal/service/tab/terminal-panel-manager';
|
||||||
import TerminalSessionManager from '@/views/terminal/service/session/terminal-session-manager';
|
import TerminalSessionManager from '@/views/terminal/service/session/terminal-session-manager';
|
||||||
import SftpTransferManager from '@/views/terminal/service/transfer/sftp-transfer-manager';
|
import TerminalTransferManager from '@/views/terminal/service/transfer/terminal-transfer-manager';
|
||||||
|
|
||||||
// 终端偏好项
|
// 终端偏好项
|
||||||
export const TerminalPreferenceItem = {
|
export const TerminalPreferenceItem = {
|
||||||
@@ -90,7 +90,7 @@ export default defineStore('terminal', {
|
|||||||
tabManager: new TerminalTabManager(),
|
tabManager: new TerminalTabManager(),
|
||||||
panelManager: new TerminalPanelManager(),
|
panelManager: new TerminalPanelManager(),
|
||||||
sessionManager: markRaw(new TerminalSessionManager()),
|
sessionManager: markRaw(new TerminalSessionManager()),
|
||||||
transferManager: new SftpTransferManager(),
|
transferManager: new TerminalTransferManager(),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { ISftpTransferManager, ITerminalPanelManager, ITerminalSessionManager, ITerminalTabManager, TerminalTheme } from '@/views/terminal/interfaces';
|
import type { ITerminalPanelManager, ITerminalSessionManager, ITerminalTabManager, ITerminalTransferManager, TerminalTheme } from '@/views/terminal/interfaces';
|
||||||
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
||||||
|
|
||||||
export interface TerminalState {
|
export interface TerminalState {
|
||||||
@@ -8,7 +8,7 @@ export interface TerminalState {
|
|||||||
tabManager: ITerminalTabManager;
|
tabManager: ITerminalTabManager;
|
||||||
panelManager: ITerminalPanelManager;
|
panelManager: ITerminalPanelManager;
|
||||||
sessionManager: ITerminalSessionManager;
|
sessionManager: ITerminalSessionManager;
|
||||||
transferManager: ISftpTransferManager;
|
transferManager: ITerminalTransferManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 终端配置
|
// 终端配置
|
||||||
|
|||||||
@@ -138,7 +138,6 @@
|
|||||||
} from '@/views/terminal/types/const';
|
} from '@/views/terminal/types/const';
|
||||||
import { computed, ref, watch, onMounted } from 'vue';
|
import { computed, ref, watch, onMounted } from 'vue';
|
||||||
import { setAutoFocus } from '@/utils/dom';
|
import { setAutoFocus } from '@/utils/dom';
|
||||||
import { Message } from '@arco-design/web-vue';
|
|
||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
import { readText } from '@/hooks/copy';
|
import { readText } from '@/hooks/copy';
|
||||||
import { useTerminalStore, useDictStore } from '@/store';
|
import { useTerminalStore, useDictStore } from '@/store';
|
||||||
@@ -150,7 +149,7 @@
|
|||||||
direction: string;
|
direction: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { preference } = useTerminalStore();
|
const { preference, transferManager } = useTerminalStore();
|
||||||
const { toOptions, getDictValue } = useDictStore();
|
const { toOptions, getDictValue } = useDictStore();
|
||||||
const { visible, setVisible } = useVisible();
|
const { visible, setVisible } = useVisible();
|
||||||
|
|
||||||
@@ -276,8 +275,8 @@
|
|||||||
|
|
||||||
// 上传文件
|
// 上传文件
|
||||||
const uploadFile = () => {
|
const uploadFile = () => {
|
||||||
// TODO 上传功能
|
transferManager.rdp.addUpload(props.session, fileList.value[0].file as File);
|
||||||
Message.warning('暂不支持文件上传, 等下版本携带');
|
fileList.value = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
// 选择文件回调
|
// 选择文件回调
|
||||||
|
|||||||
@@ -1,42 +1,84 @@
|
|||||||
import type { SftpFile } from '@/views/terminal/interfaces';
|
import type { IRdpSession, SftpFile } from '@/views/terminal/interfaces';
|
||||||
|
import type { Reactive } from 'vue';
|
||||||
|
import type Guacamole from 'guacamole-common-js';
|
||||||
|
|
||||||
|
// 终端文件传输管理器定义
|
||||||
|
export interface ITerminalTransferManager {
|
||||||
|
sftp: ISftpTransferManager;
|
||||||
|
rdp: IRdpTransferManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件传输文件项
|
||||||
|
export interface FileTransferItem {
|
||||||
|
name: string;
|
||||||
|
parentPath: string;
|
||||||
|
size: number;
|
||||||
|
file?: File;
|
||||||
|
paths?: Array<string>;
|
||||||
|
unknownSize?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件传输文件项
|
||||||
|
export interface FileTransferReactiveState {
|
||||||
|
currentSize: number,
|
||||||
|
totalSize: number;
|
||||||
|
progress: number | string;
|
||||||
|
status: string;
|
||||||
|
errorMessage?: string;
|
||||||
|
finished: boolean;
|
||||||
|
aborted: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件传输管理器定义
|
||||||
|
export interface ITransferManager {
|
||||||
|
// 传输的文件列表
|
||||||
|
tasks: Array<Reactive<FileTransferTaskType>>;
|
||||||
|
|
||||||
// sftp 传输管理器定义
|
|
||||||
export interface ISftpTransferManager {
|
|
||||||
transferList: Array<SftpTransferItem>;
|
|
||||||
// 添加上传任务
|
|
||||||
addUpload: (hostId: number, parentPath: string, files: Array<File>) => void;
|
|
||||||
// 添加下载任务
|
|
||||||
addDownload: (hostId: number, currentPath: string, files: Array<SftpFile>) => void;
|
|
||||||
// 取消传输
|
// 取消传输
|
||||||
cancelTransfer: (fileId: string) => void;
|
cancelTransfer: (fileId: string) => void;
|
||||||
// 取消全部传输
|
// 取消全部执行中的传输
|
||||||
cancelAllTransfer: () => void;
|
cancelAllTransfer: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sftp 传输处理回调定义
|
// SFTP 文件传输管理器定义
|
||||||
export interface ISftpTransferCallback {
|
export interface ISftpTransferManager extends ITransferManager {
|
||||||
// 下一分片回调
|
// 添加上传任务
|
||||||
onNextPart: () => Promise<void>;
|
addUpload: (hostId: number, parentPath: string, files: Array<File>) => Promise<void>;
|
||||||
// 开始回调
|
// 添加下载任务
|
||||||
onStart: (channelId: string, token: string) => void;
|
addDownload: (hostId: number, currentPath: string, files: Array<SftpFile>) => Promise<void>;
|
||||||
// 进度回调
|
|
||||||
onProgress: (totalSize: number | undefined, currentSize: number | undefined) => void;
|
|
||||||
// 失败回调
|
|
||||||
onError: (msg: string | undefined) => void;
|
|
||||||
// 完成回调
|
|
||||||
onFinish: () => void;
|
|
||||||
// 中断回调
|
|
||||||
onAbort: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sftp 传输处理器定义
|
// RDP 文件传输管理器定义
|
||||||
export interface ISftpTransferHandler extends ISftpTransferCallback {
|
export interface IRdpTransferManager extends ITransferManager {
|
||||||
// 类型
|
// 添加上传任务
|
||||||
|
addUpload: (session: IRdpSession, file: File) => Promise<void>;
|
||||||
|
// 添加下载任务
|
||||||
|
addDownload: (session: IRdpSession, stream: Guacamole.InputStream, mimetype: string, name: string) => void;
|
||||||
|
// 通过 sessionKey 关闭
|
||||||
|
closeBySessionKey: (sessionKey: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件传输任务类型
|
||||||
|
export type FileTransferTaskType = IFileUploadTask | IFileDownloadTask;
|
||||||
|
export type MaybeFileTransferTask = IFileUploadTask & IFileDownloadTask;
|
||||||
|
|
||||||
|
// 设置传输客户端
|
||||||
|
export interface ISetTransferClient<T> {
|
||||||
|
setClient: (client: T) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件传输任务定义
|
||||||
|
export interface IFileTransferTask {
|
||||||
type: string;
|
type: string;
|
||||||
// 是否完成
|
source: string;
|
||||||
finished: boolean;
|
fileId: string;
|
||||||
// 是否中断
|
hostId: number;
|
||||||
aborted: boolean;
|
sessionKey: string;
|
||||||
|
// 文件项
|
||||||
|
fileItem: FileTransferItem;
|
||||||
|
// 状态
|
||||||
|
state: Reactive<FileTransferReactiveState>;
|
||||||
|
|
||||||
// 开始
|
// 开始
|
||||||
start: () => void;
|
start: () => void;
|
||||||
// 完成
|
// 完成
|
||||||
@@ -45,24 +87,27 @@ export interface ISftpTransferHandler extends ISftpTransferCallback {
|
|||||||
error: () => void;
|
error: () => void;
|
||||||
// 中断
|
// 中断
|
||||||
abort: () => void;
|
abort: () => void;
|
||||||
// 是否有下一个分片
|
|
||||||
hasNextPart: () => boolean;
|
// 传输完成回调
|
||||||
|
onFinish: () => void;
|
||||||
|
// 传输失败回调
|
||||||
|
onError: (msg: string | undefined) => void;
|
||||||
|
// 传输中断回调
|
||||||
|
onAbort: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sftp 上传文件项
|
// 文件上传任务定义
|
||||||
export interface SftpTransferItem {
|
export interface IFileUploadTask extends IFileTransferTask {
|
||||||
fileId: string;
|
// 请求上传下一个分片
|
||||||
type: string;
|
onNextPart: () => Promise<void>;
|
||||||
hostId: number;
|
}
|
||||||
name: string;
|
|
||||||
parentPath: string;
|
// 文件下载任务定义
|
||||||
currentSize: number,
|
export interface IFileDownloadTask extends IFileTransferTask {
|
||||||
totalSize: number;
|
// 开始下载回调
|
||||||
progress: number | string;
|
onStart: (channelId: string, token: string) => void;
|
||||||
status: string;
|
// 下载进度回调
|
||||||
errorMessage?: string;
|
onProgress: (totalSize: number | undefined, currentSize: number | undefined) => void;
|
||||||
file: File;
|
|
||||||
paths?: Array<string>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 传输操作响应
|
// 传输操作响应
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import type { ITransferManager, FileTransferTaskType } from '@/views/terminal/interfaces';
|
||||||
|
import { TransferStatus, TerminalMessages } from '@/views/terminal/types/const';
|
||||||
|
|
||||||
|
// 传输管理器基类
|
||||||
|
export default abstract class BaseTransferManager implements ITransferManager {
|
||||||
|
|
||||||
|
public tasks: Array<FileTransferTaskType>;
|
||||||
|
|
||||||
|
protected progressIntervalId?: number;
|
||||||
|
|
||||||
|
protected constructor() {
|
||||||
|
this.tasks = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消传输
|
||||||
|
abstract cancelTransfer(fileId: string): void;
|
||||||
|
|
||||||
|
// 取消全部执行中的传输
|
||||||
|
cancelAllTransfer(): void {
|
||||||
|
// 从列表中移除非传输中的元素
|
||||||
|
this.tasks.reduceRight((_, value: FileTransferTaskType, index: number) => {
|
||||||
|
if (value.state.status !== TransferStatus.TRANSFERRING) {
|
||||||
|
this.tasks.splice(index, 1);
|
||||||
|
}
|
||||||
|
}, null as any);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算传输进度
|
||||||
|
protected calculateProgress(): void {
|
||||||
|
let count = 0;
|
||||||
|
this.tasks.forEach(task => {
|
||||||
|
const state = task.state;
|
||||||
|
if (state.totalSize !== 0
|
||||||
|
&& (state.status === TransferStatus.WAITING || state.status === TransferStatus.TRANSFERRING)
|
||||||
|
&& task.fileItem.unknownSize !== true) {
|
||||||
|
count++;
|
||||||
|
state.progress = (state.currentSize / state.totalSize * 100).toFixed(2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 如果所有任务都已结束则关闭
|
||||||
|
if (count === 0) {
|
||||||
|
clearInterval(this.progressIntervalId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重置进度定时器
|
||||||
|
protected resetProgressTimer(): void {
|
||||||
|
clearInterval(this.progressIntervalId);
|
||||||
|
this.progressIntervalId = window.setInterval(this.calculateProgress.bind(this), 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭
|
||||||
|
protected close(): void {
|
||||||
|
// 关闭传输进度
|
||||||
|
clearInterval(this.progressIntervalId);
|
||||||
|
// 进行中和等待中的文件改为失败
|
||||||
|
this.tasks.forEach(task => {
|
||||||
|
const state = task.state;
|
||||||
|
if (state.status === TransferStatus.WAITING ||
|
||||||
|
state.status === TransferStatus.TRANSFERRING) {
|
||||||
|
state.status = TransferStatus.ERROR;
|
||||||
|
state.errorMessage = TerminalMessages.sessionClosed;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
import type { FileTransferItem, IFileUploadTask, IRdpSession } from '@/views/terminal/interfaces';
|
||||||
|
import { TransferType, TransferSource, TerminalMessages, TransferStatus } from '@/views/terminal/types/const';
|
||||||
|
import { closeFileReader } from '@/utils/file';
|
||||||
|
import BaseFileTransferTask from './base-file-transfer-task';
|
||||||
|
import Guacamole from 'guacamole-common-js';
|
||||||
|
|
||||||
|
// 6048
|
||||||
|
export const PART_SIZE = Guacamole.ArrayBufferWriter.DEFAULT_BLOB_LENGTH;
|
||||||
|
|
||||||
|
// rdp 上传任务实现
|
||||||
|
export default class RdpFileUploadTask extends BaseFileTransferTask implements IFileUploadTask {
|
||||||
|
|
||||||
|
private session: IRdpSession;
|
||||||
|
private writer?: Guacamole.ArrayBufferWriter;
|
||||||
|
private stream?: Guacamole.OutputStream;
|
||||||
|
private currentPart: number;
|
||||||
|
private readonly totalPart: number;
|
||||||
|
|
||||||
|
constructor(session: IRdpSession, fileItem: FileTransferItem) {
|
||||||
|
super(TransferType.UPLOAD, TransferSource.RDP, session.info.hostId, session.sessionKey, fileItem, {});
|
||||||
|
this.session = session;
|
||||||
|
this.currentPart = 0;
|
||||||
|
this.totalPart = Math.ceil(fileItem.size / PART_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始上传
|
||||||
|
start() {
|
||||||
|
this.state.status = TransferStatus.TRANSFERRING;
|
||||||
|
try {
|
||||||
|
const file = this.fileItem.file as File;
|
||||||
|
const client = this.session.client;
|
||||||
|
// 创建文件流
|
||||||
|
this.stream = client.createFileStream(file.type, file.name);
|
||||||
|
this.writer = new Guacamole.ArrayBufferWriter(this.stream);
|
||||||
|
// 分片上传完成回调
|
||||||
|
this.writer.onack = ({ code, message }) => {
|
||||||
|
// 成功继续上传分片
|
||||||
|
if (code === Guacamole.Status.Code.SUCCESS) {
|
||||||
|
this.onNextPart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 失败关闭流
|
||||||
|
if (this.stream) {
|
||||||
|
this.stream.sendEnd();
|
||||||
|
}
|
||||||
|
// 响应错误
|
||||||
|
this.onError(message);
|
||||||
|
};
|
||||||
|
// 开始上传分片
|
||||||
|
this.onNextPart();
|
||||||
|
} catch (e) {
|
||||||
|
this.onError(TerminalMessages.fileTransferError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传中断
|
||||||
|
abort() {
|
||||||
|
this.onAbort();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传完成
|
||||||
|
finish() {
|
||||||
|
this.onFinish();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传失败
|
||||||
|
error(): void {
|
||||||
|
this.onError(undefined as unknown as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传下一个分片
|
||||||
|
async onNextPart() {
|
||||||
|
// 完成或者中断直接跳过
|
||||||
|
if (this.state.aborted || this.state.finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.hasNextPart()) {
|
||||||
|
try {
|
||||||
|
// 有下一个分片则上传
|
||||||
|
await this.uploadNextPart();
|
||||||
|
} catch (e) {
|
||||||
|
// 读取文件失败
|
||||||
|
this.error();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行上传下一分片
|
||||||
|
private async uploadNextPart() {
|
||||||
|
// 读取数据
|
||||||
|
const start = this.currentPart * PART_SIZE;
|
||||||
|
const end = Math.min(this.fileItem.size, start + PART_SIZE);
|
||||||
|
const chunk = (this.fileItem.file as File).slice(start, end);
|
||||||
|
const reader = new FileReader();
|
||||||
|
try {
|
||||||
|
const arrayBuffer = await new Promise((resolve, reject) => {
|
||||||
|
reader.onload = () => resolve(reader.result);
|
||||||
|
reader.onerror = (error) => reject(error);
|
||||||
|
reader.readAsArrayBuffer(chunk);
|
||||||
|
});
|
||||||
|
// 发送数据
|
||||||
|
this.writer?.sendData(arrayBuffer as ArrayBuffer);
|
||||||
|
this.currentPart++;
|
||||||
|
this.state.currentSize += (end - start);
|
||||||
|
} finally {
|
||||||
|
// 释放资源
|
||||||
|
closeFileReader(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传中断
|
||||||
|
onAbort() {
|
||||||
|
if (this.state.aborted || this.state.finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.state.aborted = true;
|
||||||
|
try {
|
||||||
|
if (this.session.status.connected) {
|
||||||
|
// 关闭流
|
||||||
|
if (this.stream) {
|
||||||
|
this.stream.sendEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 触发失败
|
||||||
|
this.onError(TerminalMessages.sessionClosed);
|
||||||
|
} catch (e) {
|
||||||
|
// 触发失败
|
||||||
|
this.onError(TerminalMessages.fileTransferError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传完成
|
||||||
|
onFinish() {
|
||||||
|
if (this.state.aborted || this.state.finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.state.finished = true;
|
||||||
|
this.state.progress = 100;
|
||||||
|
this.state.status = TransferStatus.SUCCESS;
|
||||||
|
// 关闭流
|
||||||
|
if (this.stream) {
|
||||||
|
this.stream.sendEnd();
|
||||||
|
}
|
||||||
|
// 释放资源
|
||||||
|
this.releaseResource();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传错误回调
|
||||||
|
onError(msg: string | undefined) {
|
||||||
|
this.state.finished = true;
|
||||||
|
this.state.status = TransferStatus.ERROR;
|
||||||
|
this.state.errorMessage = msg || TerminalMessages.fileTransferError;
|
||||||
|
// 释放资源
|
||||||
|
this.releaseResource();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否有下一个分片
|
||||||
|
private hasNextPart() {
|
||||||
|
return this.currentPart < this.totalPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 释放资源
|
||||||
|
private releaseResource() {
|
||||||
|
if (this.writer) {
|
||||||
|
this.writer.onack = null;
|
||||||
|
}
|
||||||
|
this.writer = undefined;
|
||||||
|
this.stream = undefined;
|
||||||
|
this.fileItem.file = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import type { IRdpTransferManager, IRdpSession } from '@/views/terminal/interfaces';
|
||||||
|
import type Guacamole from 'guacamole-common-js';
|
||||||
|
import { TerminalMessages } from '../../types/const';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
import BaseTransferManager from './base-transfer-manager';
|
||||||
|
import RdpFileDownloadTask from './rdp-file-download-task';
|
||||||
|
import RdpFileUploadTask from './rdp-file-upload-task';
|
||||||
|
|
||||||
|
// RDP 传输管理器实现
|
||||||
|
export default class RdpTransferManager extends BaseTransferManager implements IRdpTransferManager {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加上传任务
|
||||||
|
async addUpload(session: IRdpSession, file: File) {
|
||||||
|
Message.info(TerminalMessages.fileUploading);
|
||||||
|
// 创建任务
|
||||||
|
const task = new RdpFileUploadTask(session, {
|
||||||
|
name: file.webkitRelativePath || file.name,
|
||||||
|
parentPath: session.fileSystemName,
|
||||||
|
size: file.size,
|
||||||
|
file,
|
||||||
|
});
|
||||||
|
this.tasks.push(task);
|
||||||
|
// 开始上传
|
||||||
|
task.start();
|
||||||
|
// 开始计算进度
|
||||||
|
this.resetProgressTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加下载任务
|
||||||
|
addDownload(session: IRdpSession, stream: Guacamole.InputStream, mimetype: string, name: string) {
|
||||||
|
Message.info(TerminalMessages.fileDownloading);
|
||||||
|
// 创建任务
|
||||||
|
const task = new RdpFileDownloadTask(session, stream, mimetype, {
|
||||||
|
name,
|
||||||
|
parentPath: session.fileSystemName,
|
||||||
|
size: 0,
|
||||||
|
unknownSize: true,
|
||||||
|
});
|
||||||
|
this.tasks.push(task);
|
||||||
|
// 开始下载
|
||||||
|
task.start();
|
||||||
|
// 开始计算进度
|
||||||
|
this.resetProgressTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消传输
|
||||||
|
cancelTransfer(fileId: string): void {
|
||||||
|
const index = this.tasks.findIndex(s => s.fileId === fileId);
|
||||||
|
if (index === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 中断
|
||||||
|
this.tasks[index].abort();
|
||||||
|
// 从列表中移除
|
||||||
|
this.tasks.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过 sessionKey 关闭
|
||||||
|
closeBySessionKey(sessionKey: string): void {
|
||||||
|
this.tasks.filter(s => s.sessionKey === sessionKey)
|
||||||
|
.forEach(s => s.onError(TerminalMessages.sessionClosed));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user