🔨 修改文件传输逻辑.

This commit is contained in:
lijiahangmax
2025-06-30 22:12:28 +08:00
parent 5dcd8cbad2
commit 4468a429dd
18 changed files with 110 additions and 122 deletions

View File

@@ -48,12 +48,13 @@
import type { ISftpSession } from '@/views/terminal/interfaces';
import useVisible from '@/hooks/visible';
import { nextTick, ref } from 'vue';
import { useTerminalStore } from '@/store';
import { permission10toString } from '@/utils/file';
import { TerminalSessionTypes } from '@/views/terminal/types/const';
const props = defineProps<{
session?: ISftpSession;
}>();
const { visible, setVisible } = useVisible();
const { sessionManager } = useTerminalStore();
const sessionKey = ref();
const modRef = ref();
@@ -92,10 +93,9 @@
if (error) {
return false;
}
// 获取会话
const session = sessionManager.getSession<ISftpSession>(sessionKey.value);
if (session?.type === TerminalSessionTypes.SFTP.type) {
session.chmod(formModel.value.path, formModel.value.mod);
// 提权
if (props.session) {
props.session.chmod(formModel.value.path, formModel.value.mod);
}
} catch (e) {
return false;

View File

@@ -33,13 +33,13 @@
import type { ISftpSession } from '@/views/terminal/interfaces';
import useVisible from '@/hooks/visible';
import { nextTick, ref } from 'vue';
import { useTerminalStore } from '@/store';
import { TerminalSessionTypes } from '@/views/terminal/types/const';
const props = defineProps<{
session?: ISftpSession;
}>();
const { visible, setVisible } = useVisible();
const { sessionManager } = useTerminalStore();
const sessionKey = ref();
const touch = ref(false);
const pathRef = ref();
const formRef = ref();
@@ -48,8 +48,7 @@
});
// 打开新增
const open = (key: string, path: string, isTouch: boolean) => {
sessionKey.value = key;
const open = (path: string, isTouch: boolean) => {
if (path === '/') {
formModel.value.path = path;
} else {
@@ -73,15 +72,13 @@
if (error) {
return false;
}
// 获取会话
const session = sessionManager.getSession<ISftpSession>(sessionKey.value);
if (session?.type === TerminalSessionTypes.SFTP.type) {
if (props.session) {
if (touch.value) {
// 创建文件
session.touch(formModel.value.path);
props.session.touch(formModel.value.path);
} else {
// 创建文件夹
session.mkdir(formModel.value.path);
props.session.mkdir(formModel.value.path);
}
}
} catch (e) {

View File

@@ -41,11 +41,12 @@
import type { ISftpSession } from '@/views/terminal/interfaces';
import useVisible from '@/hooks/visible';
import { nextTick, ref } from 'vue';
import { useTerminalStore } from '@/store';
import { TerminalSessionTypes } from '@/views/terminal/types/const';
const props = defineProps<{
session?: ISftpSession;
}>();
const { visible, setVisible } = useVisible();
const { sessionManager } = useTerminalStore();
const sessionKey = ref();
const targetRef = ref();
@@ -77,10 +78,9 @@
if (error) {
return false;
}
// 获取会话
const session = sessionManager.getSession<ISftpSession>(sessionKey.value);
if (session?.type === TerminalSessionTypes.SFTP.type) {
session.move(formModel.value.path, formModel.value.target);
// 移动文件
if (props.session) {
props.session.move(formModel.value.path, formModel.value.target);
}
} catch (e) {
return false;

View File

@@ -135,7 +135,7 @@
arrow-class="terminal-tooltip-content"
content="创建文件">
<a-button class="header-action-icon icon-button"
@click="createFile">
@click="createFile(true)">
<icon-drive-file />
</a-button>
</a-tooltip>
@@ -148,7 +148,7 @@
arrow-class="terminal-tooltip-content"
content="创建文件夹">
<a-button class="header-action-icon icon-button"
@click="createDir">
@click="createFile(false)">
<icon-folder-add />
</a-button>
</a-tooltip>
@@ -174,7 +174,7 @@
arrow-class="terminal-tooltip-content"
content="上传">
<a-button class="header-action-icon icon-button"
@click="openSftpUploadModal">
@click="openUpload">
<icon-upload />
</a-button>
</a-tooltip>
@@ -204,9 +204,8 @@
<script lang="ts" setup>
import type { PathAnalysis } from '@/utils/file';
import type { ISftpSession } from '@/views/terminal/interfaces';
import { inject, nextTick, ref, watch } from 'vue';
import { nextTick, ref, watch } from 'vue';
import { getParentPath, getPathAnalysis } from '@/utils/file';
import { openSftpCreateModalKey, openSftpUploadModalKey } from '@/views/terminal/types/const';
import { useTerminalStore } from '@/store';
const props = defineProps<{
@@ -216,7 +215,7 @@
selectedFiles: Array<string>;
}>();
const emits = defineEmits(['loadFile', 'download', 'deleteFile', 'setLoading']);
const emits = defineEmits(['loadFile', 'createFile', 'upload', 'download', 'deleteFile', 'setLoading']);
const showHiddenFile = ref(false);
const analysisPaths = ref<Array<PathAnalysis>>([]);
@@ -224,10 +223,6 @@
const pathInput = ref('');
const pathInputRef = ref();
const openSftpCreateModal = inject(openSftpCreateModalKey) as (sessionKey: string, path: string, isTouch: boolean) => void;
const openSftpUploadModal = inject(openSftpUploadModalKey) as () => void;
// 监听路径变化
watch(() => props.currentPath, (path) => {
if (path) {
@@ -284,13 +279,8 @@
};
// 创建文件
const createFile = () => {
openSftpCreateModal(props.session?.sessionKey as string, props.currentPath, true);
};
// 创建文件夹
const createDir = () => {
openSftpCreateModal(props.session?.sessionKey as string, props.currentPath, false);
const createFile = (isTouch: boolean) => {
emits('createFile', isTouch);
};
// 删除选中文件
@@ -298,6 +288,11 @@
emits('deleteFile', [...props.selectedFiles]);
};
// 上传文件
const openUpload = () => {
emits('upload');
};
// 下载文件
const downloadFile = () => {
emits('download', [...props.selectedFiles], true);

View File

@@ -144,13 +144,13 @@
<script lang="ts" setup>
import type { TableData } from '@arco-design/web-vue';
import type { SftpFile, ISftpSession } from '@/views/terminal/interfaces';
import { ref, computed, watch, inject } from 'vue';
import { ref, computed, watch } from 'vue';
import { useRowSelection } from '@/hooks/table';
import { dateFormat } from '@/utils';
import { setAutoFocus } from '@/utils/dom';
import { copy } from '@/hooks/copy';
import { sftpColumns } from '@/views/terminal/types/table.columns';
import { FILE_TYPE, openSftpChmodModalKey, openSftpMoveModalKey } from '@/views/terminal/types/const';
import { FILE_TYPE } from '@/views/terminal/types/const';
const props = defineProps<{
session?: ISftpSession;
@@ -160,10 +160,7 @@
selectedFiles: Array<string>;
}>();
const emits = defineEmits(['update:selectedFiles', 'loadFile', 'editFile', 'deleteFile', 'download']);
const openSftpMoveModal = inject(openSftpMoveModalKey) as (sessionKey: string, path: string) => void;
const openSftpChmodModal = inject(openSftpChmodModalKey) as (sessionKey: string, path: string, permission: number) => void;
const emits = defineEmits(['update:selectedFiles', 'loadFile', 'moveFile', 'chmodFile', 'editFile', 'deleteFile', 'download']);
const rowSelection = useRowSelection({ width: 40 });
@@ -249,7 +246,7 @@
if (!props.session?.state.connected) {
return;
}
openSftpMoveModal(props.session?.sessionKey as string, path);
emits('moveFile', path);
};
// 文件提权
@@ -258,7 +255,7 @@
if (!props.session?.state.connected) {
return;
}
openSftpChmodModal(props.session?.sessionKey as string, path, permission);
emits('chmodFile', path, permission);
};
// 格式化文件类型

View File

@@ -80,6 +80,7 @@
<script lang="ts" setup>
import type { FileItem } from '@arco-design/web-vue';
import type { ITerminalSession } from '@/views/terminal/interfaces';
import { ref } from 'vue';
import { useTerminalStore } from '@/store';
import { Message } from '@arco-design/web-vue';
@@ -87,22 +88,21 @@
import useVisible from '@/hooks/visible';
import useLoading from '@/hooks/loading';
const props = defineProps<{
session?: ITerminalSession;
}>();
const emits = defineEmits(['closed']);
const { visible, setVisible } = useVisible();
const { loading, setLoading } = useLoading();
const { transferManager } = useTerminalStore();
const hostId = ref();
const parentPath = ref('');
const compressUpload = ref(false);
const fileList = ref<FileItem[]>([]);
// 打开
const open = (host: number, parent: string) => {
hostId.value = host;
const open = (parent: string) => {
parentPath.value = parent;
compressUpload.value = false;
setVisible(true);
};
@@ -121,7 +121,7 @@
// 获取上传的文件
const files = fileList.value.map(s => s.file as File);
// 普通上传
await transferManager.sftp.addUpload(hostId.value, parentPath.value, files);
await transferManager.sftp.addUpload(props.session as ITerminalSession, parentPath.value, files);
// 清空
handlerClear();
return true;

View File

@@ -17,7 +17,9 @@
:session="session"
@load-file="loadFiles"
@set-loading="setTableLoading"
@create-file="openCreate"
@delete-file="deleteFile"
@upload="openUpload"
@download="downloadFiles" />
<!-- 表格 -->
<sftp-table class="sftp-table-wrapper"
@@ -27,6 +29,8 @@
:loading="tableLoading"
:editor-loading="editorLoading"
@load-file="loadFiles"
@chmod-file="openChmod"
@move-file="openMove"
@edit-file="editFile"
@delete-file="deleteFile"
@download="downloadFiles" />
@@ -48,13 +52,13 @@
</template>
</a-split>
<!-- 创建文件模态框 -->
<sftp-create-modal ref="createModal" />
<sftp-create-modal ref="createModal" :session="session" />
<!-- 移动文件模态框 -->
<sftp-move-modal ref="moveModal" />
<sftp-move-modal ref="moveModal" :session="session" />
<!-- 文件提权模态框 -->
<sftp-chmod-modal ref="chmodModal" />
<sftp-chmod-modal ref="chmodModal" :session="session" />
<!-- 文件上传模态框 -->
<sftp-upload-modal ref="uploadModal" />
<sftp-upload-modal ref="uploadModal" :session="session" />
</div>
</template>
@@ -66,11 +70,10 @@
<script lang="ts" setup>
import type { ISftpSession, SftpFile, TerminalSessionTabItem } from '@/views/terminal/interfaces';
import { onMounted, onUnmounted, provide, ref } from 'vue';
import { onMounted, onUnmounted, ref } from 'vue';
import { useTerminalStore } from '@/store';
import { Message } from '@arco-design/web-vue';
import useLoading from '@/hooks/loading';
import { openSftpCreateModalKey, openSftpMoveModalKey, openSftpChmodModalKey, openSftpUploadModalKey } from '@/views/terminal/types/const';
import { getSftpFileContent, setSftpFileContent } from '@/api/terminal/terminal-sftp';
import { isString } from '@/utils/is';
import SftpTableHeader from './sftp-table-header.vue';
@@ -105,26 +108,6 @@
const chmodModal = ref();
const uploadModal = ref();
// 暴露打开创建模态框
provide(openSftpCreateModalKey, (sessionKey: string, path: string, isTouch: boolean) => {
createModal.value?.open(sessionKey, path, isTouch);
});
// 暴露打开移动模态框
provide(openSftpMoveModalKey, (sessionKey: string, path: string) => {
moveModal.value?.open(sessionKey, path);
});
// 暴露打开提权模态框
provide(openSftpChmodModalKey, (sessionKey: string, path: string, permission: number) => {
chmodModal.value?.open(sessionKey, path, permission);
});
// 暴露打开上传模态框
provide(openSftpUploadModalKey, () => {
uploadModal.value?.open(props.item.hostId, currentPath.value);
});
// 编辑文件
const editFile = (name: string, path: string) => {
setEditorLoading(true);
@@ -149,6 +132,26 @@
editorFilePath.value = '';
};
// 打开创建文件
const openCreate = (isTouch: boolean) => {
createModal.value.open(currentPath.value, isTouch);
};
// 打开文件移动
const openMove = (path: string) => {
moveModal.value.open(path);
};
// 打开文件提权
const openChmod = (path: string, permission: number) => {
chmodModal.value.open(path, permission);
};
// 打开文件上传
const openUpload = () => {
uploadModal.value.open(currentPath.value);
};
// 删除文件
const deleteFile = (paths: Array<string>) => {
if (!paths.length) {
@@ -174,7 +177,7 @@
}
// 添加普通文件到下载队列
const normalFiles = files.filter(s => !s.isDir);
await transferManager.sftp.addDownload(props.item.hostId as number, currentPath.value, normalFiles);
await transferManager.sftp.addDownload(session.value as ISftpSession, currentPath.value, normalFiles);
// 将文件夹展开普通文件
const directoryPaths = files.filter(s => s.isDir).map(s => s.path);
if (directoryPaths.length) {
@@ -282,7 +285,7 @@
if (!checkResult(result, msg)) {
return;
}
transferManager.sftp.addDownload(props.item.hostId as number, currentPath, list);
transferManager.sftp.addDownload(session.value as ISftpSession, currentPath, list);
};
// 初始化会话

View File

@@ -17,7 +17,9 @@
</div>
</ssh-context-menu>
<!-- 上传文件模态框 -->
<sftp-upload-modal ref="uploadModal" @closed="focus" />
<sftp-upload-modal ref="uploadModal"
:session="session"
@closed="focus" />
</div>
</template>

View File

@@ -35,6 +35,7 @@ export interface GuacdInitConfig {
export interface SessionHostInfo {
title: string;
name: string;
logId: number;
hostId: number;
address: string;
port: number;

View File

@@ -1,4 +1,4 @@
import type { IRdpSession, SftpFile } from '@/views/terminal/interfaces';
import type { IRdpSession, ITerminalSession, SftpFile } from '@/views/terminal/interfaces';
import type { Reactive } from 'vue';
import type Guacamole from 'guacamole-common-js';
@@ -43,9 +43,9 @@ export interface ITransferManager {
// SFTP 文件传输管理器定义
export interface ISftpTransferManager extends ITransferManager {
// 添加上传任务
addUpload: (hostId: number, parentPath: string, files: Array<File>) => Promise<void>;
addUpload: (session: ITerminalSession, parentPath: string, files: Array<File>) => Promise<void>;
// 添加下载任务
addDownload: (hostId: number, currentPath: string, files: Array<SftpFile>) => Promise<void>;
addDownload: (session: ITerminalSession, currentPath: string, files: Array<SftpFile>) => Promise<void>;
}
// RDP 文件传输管理器定义

View File

@@ -1,4 +1,4 @@
import type { ITerminalChannel, ITerminalSession } from '@/views/terminal/interfaces';
import type { ITerminalChannel, ITerminalSession, SessionHostInfo } from '@/views/terminal/interfaces';
import type { InputPayload, OutputPayload, Protocol } from '@/views/terminal/types/protocol';
import { format, InputProtocol, OutputProtocol, parse } from '@/views/terminal/types/protocol';
import { TerminalCloseCode, TerminalMessages } from '../../types/const';
@@ -66,11 +66,11 @@ export default abstract class BaseTerminalChannel<T extends ITerminalSession> im
// 处理设置信息
processSetInfo({ info }: OutputPayload) {
const data = JSON.parse(info);
const data = JSON.parse(info) as SessionHostInfo;
if (data) {
this.session.info.address = data.address;
this.session.info.port = data.port;
this.session.info.username = data.username;
Object.keys(data).forEach(k => {
(this.session.info[k as keyof SessionHostInfo] as any) = data[k as keyof SessionHostInfo];
});
}
};

View File

@@ -211,7 +211,7 @@ export default class SshSessionHandler implements ISshSessionHandler {
// 上传文件
uploadFile(): void {
this.session.config.uploadModal.open(this.session.info.hostId, '/');
this.session.config.uploadModal.open('/');
}
// ctrl + c

View File

@@ -18,9 +18,9 @@ export default abstract class BaseSession<State extends ReactiveSessionState, Ch
protected constructor(item: TerminalSessionTabItem, state: Partial<State>) {
this.type = item.type;
this.info = {
hostId: item.hostId,
title: item.title,
name: item.name,
hostId: item.hostId,
address: item.address,
} as SessionHostInfo;
this.panelIndex = item.panelIndex;

View File

@@ -1,17 +1,20 @@
import type { ISetTransferClient, FileTransferItem } from '@/views/terminal/interfaces';
import { TransferOperator, TransferStatus, TerminalMessages, TransferSource } from '../../types/const';
import type { FileTransferItem, ISetTransferClient, ITerminalSession } from '@/views/terminal/interfaces';
import { TerminalMessages, TransferOperator, TransferSource, TransferStatus } from '../../types/const';
import { getPath } from '@/utils/file';
import BaseFileTransferTask from './base-file-transfer-task';
// sftp 传输任务一定义
// sftp 传输任务基类
export default abstract class SftpBaseTransferTask extends BaseFileTransferTask implements ISetTransferClient<WebSocket> {
protected client?: WebSocket;
protected logId: number;
protected constructor(type: string,
hostId: number,
session: ITerminalSession,
fileItem: FileTransferItem) {
super(type, TransferSource.SFTP, hostId, undefined as unknown as string, fileItem, {});
super(type, TransferSource.SFTP, session.info.hostId, session.sessionKey, fileItem, {});
this.logId = session.info.logId;
}
// 设置传输客户端
@@ -25,6 +28,7 @@ export default abstract class SftpBaseTransferTask extends BaseFileTransferTask
// 发送开始信息
this.client?.send(JSON.stringify({
operator: TransferOperator.START,
logId: this.logId,
type: this.type,
hostId: this.hostId,
path: getPath(this.fileItem.parentPath + '/' + this.fileItem.name),

View File

@@ -1,4 +1,4 @@
import type { FileTransferItem, IFileDownloadTask } from '@/views/terminal/interfaces';
import type { FileTransferItem, IFileDownloadTask, ITerminalSession } from '@/views/terminal/interfaces';
import { TransferStatus, TerminalMessages } from '../../types/const';
import { getFileName, openDownloadFile } from '@/utils/file';
import { saveAs } from 'file-saver';
@@ -8,8 +8,8 @@ import SftpBaseTransferTask from './sftp-base-transfer-task';
// sftp 下载任务实现
export default class SftpFileDownloadTask extends SftpBaseTransferTask implements IFileDownloadTask {
constructor(type: string, hostId: number, fileItem: FileTransferItem) {
super(type, hostId, fileItem);
constructor(type: string, session: ITerminalSession, fileItem: FileTransferItem) {
super(type, session, fileItem);
}
// 开始回调

View File

@@ -1,4 +1,4 @@
import type { FileTransferItem, IFileUploadTask } from '@/views/terminal/interfaces';
import type { FileTransferItem, IFileUploadTask, ITerminalSession } from '@/views/terminal/interfaces';
import { closeFileReader } from '@/utils/file';
import SftpBaseTransferTask from './sftp-base-transfer-task';
@@ -11,8 +11,8 @@ export default class SftpFileUploadTask extends SftpBaseTransferTask implements
private currentPart: number;
private readonly totalPart: number;
constructor(type: string, hostId: number, fileItem: FileTransferItem) {
super(type, hostId, fileItem);
constructor(type: string, session: ITerminalSession, fileItem: FileTransferItem) {
super(type, session, fileItem);
this.currentPart = 0;
this.totalPart = Math.ceil(fileItem.size / PART_SIZE);
}

View File

@@ -2,6 +2,7 @@ import type {
FileTransferTaskType,
ISetTransferClient,
ISftpTransferManager,
ITerminalSession,
MaybeFileTransferTask,
SftpFile,
TransferOperatorResponse
@@ -28,11 +29,11 @@ export default class SftpTransferManager extends BaseTransferManager implements
}
// 添加上传任务
async addUpload(hostId: number, parentPath: string, files: Array<File>) {
async addUpload(session: ITerminalSession, parentPath: string, files: Array<File>) {
Message.info(TerminalMessages.fileUploading);
// 创建任务
for (let file of files) {
const task = new SftpFileUploadTask(TransferType.UPLOAD, hostId, {
const task = new SftpFileUploadTask(TransferType.UPLOAD, session, {
name: file.webkitRelativePath || file.name,
parentPath: parentPath,
size: file.size,
@@ -45,12 +46,12 @@ export default class SftpTransferManager extends BaseTransferManager implements
}
// 添加下载任务
async addDownload(hostId: number, currentPath: string, files: Array<SftpFile>) {
async addDownload(session: ITerminalSession, currentPath: string, files: Array<SftpFile>) {
Message.info(TerminalMessages.fileDownloading);
let pathIndex = currentPath === '/' ? 1 : currentPath.length + 1;
for (let file of files) {
// 创建任务
const task = new SftpFileDownloadTask(TransferType.DOWNLOAD, hostId, {
const task = new SftpFileDownloadTask(TransferType.DOWNLOAD, session, {
name: file.path.substring(pathIndex),
parentPath: currentPath,
size: file.size,

View File

@@ -494,18 +494,6 @@ export const PathBookmarkType = {
// 打开 extraModal key
export const openExtraModalKey = Symbol();
// 打开 sftpCreateModal key
export const openSftpCreateModalKey = Symbol();
// 打开 sftpMoveModal key
export const openSftpMoveModalKey = Symbol();
// 打开 sftpChmodModal key
export const openSftpChmodModalKey = Symbol();
// 打开 sftpUploadModal key
export const openSftpUploadModalKey = Symbol();
// 终端字体样式
export const fontFamilyKey = 'terminalFontFamily';