🐛 修复 windows 文件备份失败.
This commit is contained in:
@@ -29,10 +29,11 @@ public class SftpRemoveHandler extends AbstractTerminalHandler<SftpBaseRequest>
|
|||||||
@Override
|
@Override
|
||||||
public void handle(WebSocketSession channel, SftpBaseRequest payload) {
|
public void handle(WebSocketSession channel, SftpBaseRequest payload) {
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
// 获取会话
|
String path = payload.getPath();
|
||||||
String sessionId = payload.getSessionId();
|
String sessionId = payload.getSessionId();
|
||||||
|
// 获取会话
|
||||||
ISftpSession session = hostTerminalManager.getSession(channel.getId(), sessionId);
|
ISftpSession session = hostTerminalManager.getSession(channel.getId(), sessionId);
|
||||||
String[] paths = payload.getPath().split("\\|");
|
String[] paths = path.split("\\|");
|
||||||
log.info("SftpRemoveHandler-handle start sessionId: {}, path: {}", sessionId, Arrays.toString(paths));
|
log.info("SftpRemoveHandler-handle start sessionId: {}, path: {}", sessionId, Arrays.toString(paths));
|
||||||
Exception ex = null;
|
Exception ex = null;
|
||||||
// 删除
|
// 删除
|
||||||
@@ -53,7 +54,7 @@ public class SftpRemoveHandler extends AbstractTerminalHandler<SftpBaseRequest>
|
|||||||
.build());
|
.build());
|
||||||
// 保存操作日志
|
// 保存操作日志
|
||||||
Map<String, Object> extra = Maps.newMap();
|
Map<String, Object> extra = Maps.newMap();
|
||||||
extra.put(OperatorLogs.PATH, payload.getPath());
|
extra.put(OperatorLogs.PATH, path);
|
||||||
this.saveOperatorLog(payload, channel,
|
this.saveOperatorLog(payload, channel,
|
||||||
extra, HostTerminalOperatorType.SFTP_REMOVE,
|
extra, HostTerminalOperatorType.SFTP_REMOVE,
|
||||||
startTime, ex);
|
startTime, ex);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.orion.visor.module.asset.handler.host.transfer.utils;
|
|||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.orion.lang.exception.argument.InvalidArgumentException;
|
import com.orion.lang.exception.argument.InvalidArgumentException;
|
||||||
|
import com.orion.lang.utils.Strings;
|
||||||
import com.orion.visor.framework.common.constant.ErrorMessage;
|
import com.orion.visor.framework.common.constant.ErrorMessage;
|
||||||
import com.orion.visor.framework.websocket.core.utils.WebSockets;
|
import com.orion.visor.framework.websocket.core.utils.WebSockets;
|
||||||
import com.orion.visor.module.asset.handler.host.transfer.enums.TransferReceiverType;
|
import com.orion.visor.module.asset.handler.host.transfer.enums.TransferReceiverType;
|
||||||
@@ -64,7 +65,11 @@ public class TransferUtils {
|
|||||||
if (ex == null) {
|
if (ex == null) {
|
||||||
return null;
|
return null;
|
||||||
} else if (ex instanceof InvalidArgumentException) {
|
} else if (ex instanceof InvalidArgumentException) {
|
||||||
return ex.getMessage();
|
String message = ex.getMessage();
|
||||||
|
if (Strings.isBlank(message)) {
|
||||||
|
return ErrorMessage.OPERATE_ERROR;
|
||||||
|
}
|
||||||
|
return message;
|
||||||
} else if (ex instanceof ClientAbortException) {
|
} else if (ex instanceof ClientAbortException) {
|
||||||
return ErrorMessage.CLIENT_ABORT;
|
return ErrorMessage.CLIENT_ABORT;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,10 @@ public class HostSftpServiceImpl implements HostSftpService {
|
|||||||
vo.setHostId(extra.getLong(ExtraFieldConst.HOST_ID));
|
vo.setHostId(extra.getLong(ExtraFieldConst.HOST_ID));
|
||||||
vo.setHostName(extra.getString(ExtraFieldConst.HOST_NAME));
|
vo.setHostName(extra.getString(ExtraFieldConst.HOST_NAME));
|
||||||
vo.setHostAddress(extra.getString(ExtraFieldConst.ADDRESS));
|
vo.setHostAddress(extra.getString(ExtraFieldConst.ADDRESS));
|
||||||
vo.setPaths(extra.getString(ExtraFieldConst.PATH).split("\\|"));
|
String[] paths = Optional.ofNullable(extra.getString(ExtraFieldConst.PATH))
|
||||||
|
.map(p -> p.split("\\|"))
|
||||||
|
.orElse(new String[0]);
|
||||||
|
vo.setPaths(paths);
|
||||||
vo.setExtra(extra);
|
vo.setExtra(extra);
|
||||||
return vo;
|
return vo;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package com.orion.visor.module.asset.utils;
|
package com.orion.visor.module.asset.utils;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.jcraft.jsch.SftpException;
|
||||||
import com.orion.lang.utils.Booleans;
|
import com.orion.lang.utils.Booleans;
|
||||||
import com.orion.lang.utils.Strings;
|
import com.orion.lang.utils.Strings;
|
||||||
|
import com.orion.lang.utils.io.Files1;
|
||||||
import com.orion.net.host.sftp.SftpExecutor;
|
import com.orion.net.host.sftp.SftpExecutor;
|
||||||
import com.orion.net.host.sftp.SftpFile;
|
import com.orion.net.host.sftp.SftpFile;
|
||||||
import com.orion.visor.module.asset.define.config.AppSftpConfig;
|
import com.orion.visor.module.asset.define.config.AppSftpConfig;
|
||||||
@@ -39,7 +41,12 @@ public class SftpUtils {
|
|||||||
SftpFileBackupParams backupParams = new SftpFileBackupParams(file.getName(), System.currentTimeMillis());
|
SftpFileBackupParams backupParams = new SftpFileBackupParams(file.getName(), System.currentTimeMillis());
|
||||||
String target = Strings.format(config.getBackupFileName(), JSON.parseObject(JSON.toJSONString(backupParams)));
|
String target = Strings.format(config.getBackupFileName(), JSON.parseObject(JSON.toJSONString(backupParams)));
|
||||||
// 移动
|
// 移动
|
||||||
executor.move(path, target);
|
try {
|
||||||
|
executor.getChannel().rename(path, Files1.getPath(Files1.normalize(Files1.getPath(path + "/../" + target))));
|
||||||
|
} catch (SftpException ignored) {
|
||||||
|
}
|
||||||
|
// FIXME kit
|
||||||
|
// executor.move(path, target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export interface HostSftpLogQueryResponse extends TableData {
|
|||||||
export interface HostSftpLogExtra {
|
export interface HostSftpLogExtra {
|
||||||
mod: number;
|
mod: number;
|
||||||
target: string;
|
target: string;
|
||||||
|
maxCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -107,10 +107,14 @@
|
|||||||
{{ getDictValue(sftpOperatorTypeKey, record.type) }}
|
{{ getDictValue(sftpOperatorTypeKey, record.type) }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- 文件数量 -->
|
||||||
|
<template #fileCount="{ record }">
|
||||||
|
<b class="span-blue">{{ record.paths.length }}</b> 个
|
||||||
|
</template>
|
||||||
<!-- 操作文件 -->
|
<!-- 操作文件 -->
|
||||||
<template #paths="{ record }">
|
<template #paths="{ record }">
|
||||||
<div class="paths-wrapper">
|
<div class="paths-wrapper">
|
||||||
<span v-for="path in record.paths"
|
<span v-for="path in record.paths.slice(0, record.extra.maxCount)"
|
||||||
class="path-wrapper text-ellipsis text-copy"
|
class="path-wrapper text-ellipsis text-copy"
|
||||||
:title="path"
|
:title="path"
|
||||||
@click="copy(path)">
|
@click="copy(path)">
|
||||||
@@ -125,6 +129,13 @@
|
|||||||
提权 {{ record.extra?.mod }} {{ permission10toString(record.extra?.mod as number) }}
|
提权 {{ record.extra?.mod }} {{ permission10toString(record.extra?.mod as number) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 查看更多-->
|
||||||
|
<div v-if="record.paths.length > record.extra.maxCount"
|
||||||
|
class="paths-wrapper span-blue pointer"
|
||||||
|
title="查看更多"
|
||||||
|
@click="() => record.extra.maxCount = record.paths.length">
|
||||||
|
查看更多
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<!-- 执行结果 -->
|
<!-- 执行结果 -->
|
||||||
<template #result="{ record }">
|
<template #result="{ record }">
|
||||||
@@ -175,7 +186,7 @@
|
|||||||
import type { HostSftpLogQueryRequest, HostSftpLogQueryResponse } from '@/api/asset/host-sftp';
|
import type { HostSftpLogQueryRequest, HostSftpLogQueryResponse } from '@/api/asset/host-sftp';
|
||||||
import { reactive, ref, onMounted } from 'vue';
|
import { reactive, ref, onMounted } from 'vue';
|
||||||
import { getHostSftpLogPage, deleteHostSftpLog } from '@/api/asset/host-sftp';
|
import { getHostSftpLogPage, deleteHostSftpLog } from '@/api/asset/host-sftp';
|
||||||
import { sftpOperatorTypeKey, sftpOperatorResultKey, SftpOperatorType } from '../types/const';
|
import { sftpOperatorTypeKey, sftpOperatorResultKey, SftpOperatorType, showPathMaxCount } from '../types/const';
|
||||||
import { usePagination, useRowSelection } from '@/types/table';
|
import { usePagination, useRowSelection } from '@/types/table';
|
||||||
import { useDictStore } from '@/store';
|
import { useDictStore } from '@/store';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
@@ -186,14 +197,13 @@
|
|||||||
import UserSelector from '@/components/user/user/selector/index.vue';
|
import UserSelector from '@/components/user/user/selector/index.vue';
|
||||||
import HostSelector from '@/components/asset/host/selector/index.vue';
|
import HostSelector from '@/components/asset/host/selector/index.vue';
|
||||||
|
|
||||||
const tableRenderData = ref<HostSftpLogQueryResponse[]>([]);
|
|
||||||
const selectedKeys = ref<number[]>([]);
|
|
||||||
|
|
||||||
const pagination = usePagination();
|
const pagination = usePagination();
|
||||||
const rowSelection = useRowSelection();
|
const rowSelection = useRowSelection();
|
||||||
const { loading, setLoading } = useLoading();
|
const { loading, setLoading } = useLoading();
|
||||||
const { toOptions, getDictValue } = useDictStore();
|
const { toOptions, getDictValue } = useDictStore();
|
||||||
|
|
||||||
|
const tableRenderData = ref<Array<HostSftpLogQueryResponse>>([]);
|
||||||
|
const selectedKeys = ref<Array<number>>([]);
|
||||||
const formModel = reactive<HostSftpLogQueryRequest>({
|
const formModel = reactive<HostSftpLogQueryRequest>({
|
||||||
userId: undefined,
|
userId: undefined,
|
||||||
hostId: undefined,
|
hostId: undefined,
|
||||||
@@ -206,7 +216,12 @@
|
|||||||
const doFetchTableData = async (request: HostSftpLogQueryRequest) => {
|
const doFetchTableData = async (request: HostSftpLogQueryRequest) => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
// 查询
|
||||||
const { data } = await getHostSftpLogPage(request);
|
const { data } = await getHostSftpLogPage(request);
|
||||||
|
// 设置最大数量
|
||||||
|
data.rows.forEach(s => {
|
||||||
|
s.extra.maxCount = showPathMaxCount;
|
||||||
|
});
|
||||||
tableRenderData.value = data.rows;
|
tableRenderData.value = data.rows;
|
||||||
pagination.total = data.total;
|
pagination.total = data.total;
|
||||||
pagination.current = request.page;
|
pagination.current = request.page;
|
||||||
@@ -240,13 +255,11 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 删除当前行
|
// 删除当前行
|
||||||
const deleteRow = async ({ id }: {
|
const deleteRow = async (record: HostSftpLogQueryResponse) => {
|
||||||
id: number
|
|
||||||
}) => {
|
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
// 调用删除接口
|
// 调用删除接口
|
||||||
await deleteHostSftpLog([id]);
|
await deleteHostSftpLog([record.id]);
|
||||||
Message.success('删除成功');
|
Message.success('删除成功');
|
||||||
selectedKeys.value = [];
|
selectedKeys.value = [];
|
||||||
// 重新加载数据
|
// 重新加载数据
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ export const SftpOperatorType = {
|
|||||||
SFTP_CHMOD: 'host-terminal:sftp-chmod',
|
SFTP_CHMOD: 'host-terminal:sftp-chmod',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 最大展示数量
|
||||||
|
export const showPathMaxCount = 5;
|
||||||
|
|
||||||
// sftp 操作类型 字典项
|
// sftp 操作类型 字典项
|
||||||
export const sftpOperatorTypeKey = 'sftpOperatorType';
|
export const sftpOperatorTypeKey = 'sftpOperatorType';
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ const columns = [
|
|||||||
slotName: 'type',
|
slotName: 'type',
|
||||||
width: 116,
|
width: 116,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
}, {
|
||||||
|
title: '文件数量',
|
||||||
|
dataIndex: 'fileCount',
|
||||||
|
slotName: 'fileCount',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
}, {
|
}, {
|
||||||
title: '操作文件',
|
title: '操作文件',
|
||||||
dataIndex: 'paths',
|
dataIndex: 'paths',
|
||||||
|
|||||||
@@ -50,7 +50,11 @@
|
|||||||
// 打开新增
|
// 打开新增
|
||||||
const open = (session: string, path: string, isTouch: boolean) => {
|
const open = (session: string, path: string, isTouch: boolean) => {
|
||||||
sessionId.value = session;
|
sessionId.value = session;
|
||||||
formModel.value.path = path;
|
if (path === '/') {
|
||||||
|
formModel.value.path = path;
|
||||||
|
} else {
|
||||||
|
formModel.value.path = path + '/';
|
||||||
|
}
|
||||||
touch.value = isTouch;
|
touch.value = isTouch;
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
// 自动聚焦
|
// 自动聚焦
|
||||||
|
|||||||
@@ -285,12 +285,12 @@
|
|||||||
|
|
||||||
// 创建文件
|
// 创建文件
|
||||||
const createFile = () => {
|
const createFile = () => {
|
||||||
openSftpCreateModal(props.session?.sessionId as string, props.currentPath + '/', true);
|
openSftpCreateModal(props.session?.sessionId as string, props.currentPath, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 创建文件夹
|
// 创建文件夹
|
||||||
const createDir = () => {
|
const createDir = () => {
|
||||||
openSftpCreateModal(props.session?.sessionId as string, props.currentPath + '/', false);
|
openSftpCreateModal(props.session?.sessionId as string, props.currentPath, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 删除选中文件
|
// 删除选中文件
|
||||||
|
|||||||
@@ -106,7 +106,8 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!fileList.value.length) {
|
if (!fileList.value.length) {
|
||||||
return true;
|
Message.error('请选择文件');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
// 添加到上传列表
|
// 添加到上传列表
|
||||||
const files = fileList.value.map(s => s.file as File);
|
const files = fileList.value.map(s => s.file as File);
|
||||||
@@ -168,6 +169,10 @@
|
|||||||
margin-right: 0 !important;
|
margin-right: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.arco-upload-list-item-name-link) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.arco-upload-list-item-name-text) {
|
:deep(.arco-upload-list-item-name-text) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { TransferOperatorType, TransferStatus } from '../types/terminal.const';
|
|||||||
import { getFileName, getPath } from '@/utils/file';
|
import { getFileName, getPath } from '@/utils/file';
|
||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
|
|
||||||
// sftp 上传器实现
|
// sftp 下载器实现
|
||||||
export default class SftpTransferDownloader implements ISftpTransferDownloader {
|
export default class SftpTransferDownloader implements ISftpTransferDownloader {
|
||||||
|
|
||||||
public abort: boolean;
|
public abort: boolean;
|
||||||
|
|||||||
Reference in New Issue
Block a user