🔨 下载文件.
This commit is contained in:
@@ -123,6 +123,14 @@ public enum InputTypeEnum {
|
||||
new String[]{"type", "sessionId", "path", "mod"},
|
||||
SftpChangeModRequest.class),
|
||||
|
||||
/**
|
||||
* SFTP 下载文件夹 flat
|
||||
*/
|
||||
SFTP_DOWNLOAD_DIRECTORY_FLAT("df",
|
||||
SftpDownloadDirectoryFlatHandler.class,
|
||||
new String[]{"type", "sessionId", "currentPath", "path"},
|
||||
SftpDownloadDirectoryFlatRequest.class),
|
||||
|
||||
/**
|
||||
* SFTP 获取内容
|
||||
*/
|
||||
@@ -139,10 +147,6 @@ public enum InputTypeEnum {
|
||||
new String[]{"type", "sessionId", "path", "content"},
|
||||
SftpSetContentRequest.class),
|
||||
|
||||
// TODO
|
||||
// UPLOAD
|
||||
// DOWNLOAD
|
||||
|
||||
;
|
||||
|
||||
private static final char SEPARATOR = '|';
|
||||
|
||||
@@ -75,6 +75,11 @@ public enum OutputTypeEnum {
|
||||
*/
|
||||
SFTP_CHMOD("cm", "${type}|${sessionId}|${result}|${msg}"),
|
||||
|
||||
/**
|
||||
* SFTP 下载文件夹 flat
|
||||
*/
|
||||
SFTP_DOWNLOAD_DIRECTORY_FLAT("df", "${type}|${sessionId}|${currentPath}|${body}"),
|
||||
|
||||
/**
|
||||
* SFTP 获取文件内容
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.orion.ops.module.asset.handler.host.terminal.handler;
|
||||
|
||||
import com.orion.ops.framework.common.enums.BooleanBit;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.model.request.SftpDownloadDirectoryFlatRequest;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.model.response.SftpDownloadDirectoryFlatResponse;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.session.ISftpSession;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
/**
|
||||
* sftp 下载文件夹 flat
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/19 11:13
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SftpDownloadDirectoryFlatHandler extends AbstractTerminalHandler<SftpDownloadDirectoryFlatRequest> {
|
||||
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, SftpDownloadDirectoryFlatRequest payload) {
|
||||
// 获取会话
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), payload.getSessionId());
|
||||
String path = payload.getPath();
|
||||
log.info("SftpDownloadDirectoryFlatHandler-handle session: {}, path: {}", payload.getSessionId(), path);
|
||||
Exception ex = null;
|
||||
// 获取文件夹内的全部文件
|
||||
try {
|
||||
// TODO
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("SftpDownloadDirectoryFlatHandler-handle error", e);
|
||||
ex = e;
|
||||
}
|
||||
// 返回
|
||||
this.send(channel,
|
||||
OutputTypeEnum.SFTP_DOWNLOAD_DIRECTORY_FLAT,
|
||||
SftpDownloadDirectoryFlatResponse.builder()
|
||||
.sessionId(payload.getSessionId())
|
||||
.currentPath(payload.getPath())
|
||||
// TODO
|
||||
.body("")
|
||||
.result(BooleanBit.of(ex == null).getValue())
|
||||
.msg(this.getErrorMessage(ex))
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -103,6 +103,7 @@ public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConn
|
||||
try {
|
||||
// 连接配置
|
||||
TerminalConfig config = TerminalConfig.builder()
|
||||
.hostId(connect.getHostId())
|
||||
.charset(connect.getCharset())
|
||||
.fileNameCharset(connect.getFileNameCharset())
|
||||
.fileContentCharset(connect.getFileContentCharset())
|
||||
|
||||
@@ -22,6 +22,9 @@ import lombok.NoArgsConstructor;
|
||||
@Schema(name = "TerminalConfig", description = "主机终端连接参数")
|
||||
public class TerminalConfig {
|
||||
|
||||
@Schema(description = "主机id")
|
||||
private Long hostId;
|
||||
|
||||
@Schema(description = "cols")
|
||||
private Integer cols;
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.orion.ops.module.asset.handler.host.terminal.model.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* sftp 下载文件夹 flat 实体对象
|
||||
* <p>
|
||||
* i|eff00a1|currentPath|path
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/6 13:31
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "SftpDownloadDirectoryFlatRequest", description = "sftp 下载文件夹 flat 实体对象")
|
||||
public class SftpDownloadDirectoryFlatRequest extends SftpBaseRequest {
|
||||
|
||||
@Schema(description = "当前路径")
|
||||
private Integer currentPath;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.orion.ops.module.asset.handler.host.terminal.model.response;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* sftp 下载文件夹 flat 实体对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/6 16:20
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "SftpDownloadDirectoryFlatResponse", description = "sftp 下载文件夹 flat 实体对象")
|
||||
public class SftpDownloadDirectoryFlatResponse extends SftpBaseResponse {
|
||||
|
||||
@Schema(description = "currentPath")
|
||||
private String currentPath;
|
||||
|
||||
@Schema(description = "body")
|
||||
private String body;
|
||||
|
||||
}
|
||||
@@ -32,8 +32,6 @@ import java.util.stream.Collectors;
|
||||
@Slf4j
|
||||
public class SftpSession extends TerminalSession implements ISftpSession {
|
||||
|
||||
private final TerminalConfig config;
|
||||
|
||||
private final SessionStore sessionStore;
|
||||
|
||||
private SftpExecutor executor;
|
||||
@@ -42,9 +40,8 @@ public class SftpSession extends TerminalSession implements ISftpSession {
|
||||
WebSocketSession channel,
|
||||
SessionStore sessionStore,
|
||||
TerminalConfig config) {
|
||||
super(sessionId, channel);
|
||||
super(sessionId, channel, config);
|
||||
this.sessionStore = sessionStore;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,8 +9,8 @@ import com.orion.ops.module.asset.define.AssetThreadPools;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.constant.TerminalMessage;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalConfig;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalCloseResponse;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.model.response.SshOutputResponse;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalCloseResponse;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
@@ -29,8 +29,6 @@ import java.io.InputStream;
|
||||
@Slf4j
|
||||
public class SshSession extends TerminalSession implements ISshSession {
|
||||
|
||||
private final TerminalConfig config;
|
||||
|
||||
private final SessionStore sessionStore;
|
||||
|
||||
private ShellExecutor executor;
|
||||
@@ -42,9 +40,8 @@ public class SshSession extends TerminalSession implements ISshSession {
|
||||
WebSocketSession channel,
|
||||
SessionStore sessionStore,
|
||||
TerminalConfig config) {
|
||||
super(sessionId, channel);
|
||||
super(sessionId, channel, config);
|
||||
this.sessionStore = sessionStore;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.orion.ops.module.asset.handler.host.terminal.session;
|
||||
|
||||
import com.orion.ops.module.asset.enums.HostConnectStatusEnum;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalConfig;
|
||||
import com.orion.ops.module.asset.service.HostConnectLogService;
|
||||
import com.orion.spring.SpringHolder;
|
||||
import lombok.Getter;
|
||||
@@ -22,11 +23,14 @@ public abstract class TerminalSession implements ITerminalSession {
|
||||
|
||||
protected final WebSocketSession channel;
|
||||
|
||||
protected final TerminalConfig config;
|
||||
|
||||
protected volatile boolean close;
|
||||
|
||||
public TerminalSession(String sessionId, WebSocketSession channel) {
|
||||
public TerminalSession(String sessionId, WebSocketSession channel, TerminalConfig config) {
|
||||
this.sessionId = sessionId;
|
||||
this.channel = channel;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -190,7 +190,7 @@
|
||||
selectedFiles: Array<string>
|
||||
}>();
|
||||
|
||||
const emits = defineEmits(['loadFile']);
|
||||
const emits = defineEmits(['update:selectedFiles', 'loadFile', 'download']);
|
||||
|
||||
const showHiddenFile = ref(false);
|
||||
const analysisPaths = ref<Array<PathAnalysis>>([]);
|
||||
@@ -268,8 +268,8 @@
|
||||
|
||||
// 下载文件
|
||||
const downloadFile = () => {
|
||||
// TODO
|
||||
console.log(props.selectedFiles);
|
||||
emits('download', [...props.selectedFiles]);
|
||||
emits('update:selectedFiles', []);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
</span>
|
||||
</a-tooltip>
|
||||
<!-- 编辑内容 -->
|
||||
<a-tooltip v-if="canEditable(record.size, record.attr)"
|
||||
<a-tooltip v-if="canEditable(record.size, record.isDir)"
|
||||
position="top"
|
||||
:mini="true"
|
||||
:overlay-inverse="true"
|
||||
@@ -161,7 +161,7 @@
|
||||
selectedFiles: Array<string>;
|
||||
}>();
|
||||
|
||||
const emits = defineEmits(['update:selectedFiles', 'loadFile', 'editFile']);
|
||||
const emits = defineEmits(['update:selectedFiles', 'loadFile', 'editFile', 'download']);
|
||||
|
||||
const openSftpMoveModal = inject(openSftpMoveModalKey) as (sessionId: string, path: string) => void;
|
||||
const openSftpChmodModal = inject(openSftpChmodModalKey) as (sessionId: string, path: string, permission: number) => void;
|
||||
@@ -204,12 +204,9 @@
|
||||
};
|
||||
|
||||
// 是否可编辑
|
||||
const canEditable = (size: number, attr: string) => {
|
||||
const typeValue = formatFileType(attr).value;
|
||||
// 非文件夹和链接文件 并且文件小于 配置大小(MB) 可以编辑
|
||||
return FILE_TYPE.DIRECTORY.value !== typeValue
|
||||
&& FILE_TYPE.LINK_FILE.value !== typeValue
|
||||
&& size <= previewSize * 1024 * 1024;
|
||||
const canEditable = (size: number, isDir: boolean) => {
|
||||
// 非文件夹并且文件小于 配置大小(MB) 可以编辑
|
||||
return !isDir && size <= previewSize * 1024 * 1024;
|
||||
};
|
||||
|
||||
// 点击文件名称
|
||||
@@ -235,8 +232,7 @@
|
||||
|
||||
// 下载文件
|
||||
const downloadFile = (path: string) => {
|
||||
// TODO
|
||||
console.log(path);
|
||||
emits('download', [path]);
|
||||
};
|
||||
|
||||
// 移动文件
|
||||
|
||||
@@ -112,20 +112,8 @@
|
||||
return true;
|
||||
}
|
||||
// 添加到上传列表
|
||||
const files = fileList.value.map(s => {
|
||||
return {
|
||||
fileId: nextId(10),
|
||||
type: TransferType.UPLOAD,
|
||||
hostId: hostId.value,
|
||||
name: s.file.webkitRelativePath || s.file.name,
|
||||
currentSize: 0,
|
||||
totalSize: s.file.size,
|
||||
status: TransferStatus.WAITING,
|
||||
parentPath: parentPath.value,
|
||||
file: s.file
|
||||
};
|
||||
});
|
||||
transferManager.addTransfer(files);
|
||||
const files = fileList.value.map(s => s.file);
|
||||
transferManager.addUpload(hostId.value, parentPath.value, files);
|
||||
Message.success('已开始上传, 点击右侧传输列表查看进度');
|
||||
// 清空
|
||||
handlerClear();
|
||||
|
||||
@@ -11,10 +11,11 @@
|
||||
:hide-icon="true">
|
||||
<!-- 表头 -->
|
||||
<sftp-table-header class="sftp-table-header"
|
||||
v-model:selected-files="selectFiles"
|
||||
:current-path="currentPath"
|
||||
:session="session"
|
||||
:selected-files="selectFiles"
|
||||
@load-file="loadFiles" />
|
||||
@load-file="loadFiles"
|
||||
@download="downloadFiles" />
|
||||
<!-- 表格 -->
|
||||
<sftp-table class="sftp-table-wrapper"
|
||||
v-model:selected-files="selectFiles"
|
||||
@@ -22,7 +23,8 @@
|
||||
:list="fileList"
|
||||
:loading="tableLoading"
|
||||
@load-file="loadFiles"
|
||||
@edit-file="editFile" />
|
||||
@edit-file="editFile"
|
||||
@download="downloadFiles" />
|
||||
</a-spin>
|
||||
</template>
|
||||
<template #second v-if="editorView">
|
||||
@@ -78,7 +80,7 @@
|
||||
tab: TerminalTabItem
|
||||
}>();
|
||||
|
||||
const { preference, sessionManager } = useTerminalStore();
|
||||
const { preference, sessionManager, transferManager } = useTerminalStore();
|
||||
const { loading: tableLoading, setLoading: setTableLoading } = useLoading(true);
|
||||
const { loading: editorLoading, setLoading: setEditorLoading } = useLoading();
|
||||
|
||||
@@ -140,6 +142,25 @@
|
||||
editorFilePath.value = '';
|
||||
};
|
||||
|
||||
// 下载文件
|
||||
const downloadFiles = (paths: Array<string>) => {
|
||||
if (!paths.length) {
|
||||
return paths;
|
||||
}
|
||||
Message.success('已开始下载, 点击右侧传输列表查看进度');
|
||||
// 映射为文件
|
||||
const files = fileList.value.filter(s => paths.includes(s.path))
|
||||
.map(s => {
|
||||
return { ...s };
|
||||
});
|
||||
// 添加普通文件到下载队列
|
||||
const normalFiles = files.filter(s => !s.isDir);
|
||||
transferManager.addDownload(props.tab.hostId as number, currentPath.value, normalFiles);
|
||||
// 将文件夹转为普通文件
|
||||
const directoryFiles = files.filter(s => s.isDir);
|
||||
// TODO
|
||||
};
|
||||
|
||||
// 连接成功回调
|
||||
const connectCallback = () => {
|
||||
loadFiles(undefined);
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
<!-- 传输状态 -->
|
||||
<div class="transfer-item-right-progress">
|
||||
<!-- 等待传输 -->
|
||||
<icon-loading v-if="item.status === TransferStatus.WAITING" />
|
||||
<icon-clock-circle v-if="item.status === TransferStatus.WAITING" />
|
||||
<!-- 传输进度 -->
|
||||
<a-progress v-else
|
||||
type="circle"
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { ISftpTransferManager, ISftpTransferUploader, SftpTransferItem } from '../types/terminal.type';
|
||||
import { TransferOperatorResponse } from '../types/terminal.type';
|
||||
import { SftpFile, TransferOperatorResponse } from '../types/terminal.type';
|
||||
import { TransferReceiverType, TransferStatus, TransferType } from '../types/terminal.const';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { getTerminalAccessToken } from '@/api/asset/host-terminal';
|
||||
import SftpTransferUploader from '@/views/host/terminal/handler/sftp-transfer-uploader';
|
||||
import { nextId } from '@/utils';
|
||||
|
||||
export const wsBase = import.meta.env.VITE_WS_BASE_URL;
|
||||
|
||||
@@ -25,8 +26,43 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
this.transferList = [];
|
||||
}
|
||||
|
||||
// 添加传输
|
||||
addTransfer(items: Array<SftpTransferItem>): void {
|
||||
// 添加上传任务
|
||||
addUpload(hostId: number, parentPath: string, files: Array<File>) {
|
||||
// 转为上传任务
|
||||
const items = files.map(s => {
|
||||
return {
|
||||
fileId: nextId(10),
|
||||
type: TransferType.UPLOAD,
|
||||
hostId: hostId,
|
||||
name: s.webkitRelativePath || s.name,
|
||||
currentSize: 0,
|
||||
totalSize: s.size,
|
||||
status: TransferStatus.WAITING,
|
||||
parentPath: parentPath,
|
||||
file: s
|
||||
};
|
||||
});
|
||||
this.transferList.push(...items);
|
||||
// 开始传输
|
||||
if (!this.run) {
|
||||
this.openClient();
|
||||
}
|
||||
}
|
||||
|
||||
// 添加下载任务
|
||||
addDownload(hostId: number, currentPath: string, files: Array<SftpFile>) {
|
||||
// 转为下载文件
|
||||
const items = files.map(s => {
|
||||
return {
|
||||
fileId: nextId(10),
|
||||
type: TransferType.DOWNLOAD,
|
||||
hostId: hostId,
|
||||
name: s.path.substring(currentPath.length),
|
||||
currentSize: 0,
|
||||
totalSize: s.size,
|
||||
status: TransferStatus.WAITING,
|
||||
};
|
||||
}) as Array<SftpTransferItem>;
|
||||
this.transferList.push(...items);
|
||||
// 开始传输
|
||||
if (!this.run) {
|
||||
@@ -85,6 +121,10 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
// 传输下一条任务
|
||||
private transferNextItem() {
|
||||
this.currentUploader = undefined;
|
||||
// 释放内存
|
||||
if (this.currentItem) {
|
||||
this.currentItem.file = null as unknown as File;
|
||||
}
|
||||
// 获取任务
|
||||
this.currentItem = this.transferList.find(s => s.status === TransferStatus.WAITING);
|
||||
if (this.currentItem) {
|
||||
|
||||
@@ -372,8 +372,10 @@ export interface SftpFile {
|
||||
// sftp 传输管理器定义
|
||||
export interface ISftpTransferManager {
|
||||
transferList: Array<SftpTransferItem>;
|
||||
// 添加传输
|
||||
addTransfer: (items: Array<SftpTransferItem>) => void;
|
||||
// 添加上传任务
|
||||
addUpload: (hostId: number, parentPath: string, files: Array<File>) => void;
|
||||
// 添加下载任务
|
||||
addDownload: (hostId: number, currentPath: string, files: Array<SftpFile>) => void;
|
||||
// 取消传输
|
||||
cancelTransfer: (fileId: string) => void;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user