完善编排的文件操作模块

This commit is contained in:
fofolee 2025-03-23 13:49:17 +08:00
parent 3cec2f94f2
commit 9bcea1e575
5 changed files with 277 additions and 445 deletions

View File

@ -126,6 +126,4 @@ async function archive(
});
}
module.exports = {
archive,
};
module.exports = archive;

View File

@ -7,23 +7,22 @@ const path = require("path");
* @param {string} config.filePath 文件路径
* @param {string} config.encoding 编码方式
* @param {string} config.readMode 读取模式
* @param {string} config.flag 读取标志
* @param {number} [config.start] 起始位置
* @param {number} [config.length] 读取长度
* @returns {Promise<string|Buffer>} 文件内容
*/
async function read(config) {
const { filePath, encoding, readMode, flag, start, length } = config;
const { filePath, encoding, readMode, start, length } = config;
if (readMode === "all") {
return await fs.readFile(filePath, { encoding, flag });
return await fs.readFile(filePath, { encoding });
} else if (
readMode === "start" &&
typeof start === "number" &&
typeof length === "number"
) {
// 指定位置读取
const fileHandle = await fs.open(filePath, flag || "r");
const fileHandle = await fs.open(filePath, "r");
try {
const buffer = Buffer.alloc(length);
const { bytesRead } = await fileHandle.read(buffer, 0, length, start);
@ -39,12 +38,11 @@ async function read(config) {
// 按行读取,暂时使用全部读取然后分行的方式
const content = await fs.readFile(filePath, {
encoding: encoding || "utf8",
flag,
});
return content.split(/\r?\n/);
} else {
// 默认使用全部读取
return await fs.readFile(filePath, { encoding, flag });
return await fs.readFile(filePath, { encoding });
}
}
@ -85,20 +83,12 @@ async function write(config) {
* 文件删除操作
*/
async function remove(config) {
const { filePath, recursive, force, targetType } = config;
const { filePath, recursive, force } = config;
// 检查文件是否存在
try {
const stats = await fs.lstat(filePath);
// 检查目标类型
if (targetType === "file" && !stats.isFile()) {
throw new Error("目标不是文件");
}
if (targetType === "directory" && !stats.isDirectory()) {
throw new Error("目标不是目录");
}
// 执行删除操作
if (stats.isDirectory()) {
await fs.rm(filePath, { recursive, force });
@ -115,39 +105,16 @@ async function remove(config) {
}
/**
* 文件管理操作
* 文件权限操作
*/
async function manage(config) {
const {
filePath,
manageOperation,
newPath,
mode,
uid,
gid,
recursive,
targetType,
} = config;
async function permission(config) {
const { filePath, operationType, mode, uid, gid, recursive } = config;
// 检查文件是否存在
const stats = await fs.lstat(filePath);
try {
// 检查文件是否存在
const stats = await fs.lstat(filePath);
// 检查目标类型
if (targetType === "file" && !stats.isFile()) {
throw new Error("目标不是文件");
}
if (targetType === "directory" && !stats.isDirectory()) {
throw new Error("目标不是目录");
}
switch (manageOperation) {
case "rename":
// 确保目标目录存在
await fs.mkdir(path.dirname(newPath), { recursive: true });
await fs.rename(filePath, newPath);
break;
case "chmod":
if (operationType === "chmod") {
if (recursive && stats.isDirectory()) {
const walk = async (dir) => {
const files = await fs.readdir(dir);
@ -166,9 +133,7 @@ async function manage(config) {
} else {
await fs.chmod(filePath, parseInt(mode, 8));
}
break;
case "chown":
} else if (operationType === "chown") {
if (recursive && stats.isDirectory()) {
await fs.chown(filePath, uid, gid);
const walk = async (dir) => {
@ -188,10 +153,71 @@ async function manage(config) {
} else {
await fs.chown(filePath, uid, gid);
}
break;
} else {
throw new Error(`不支持的操作类型: ${operationType}`);
}
} catch (error) {
if (error.code === "ENOENT") {
throw new Error("文件或目录不存在");
}
throw error;
}
}
default:
throw new Error(`不支持的操作类型: ${manageOperation}`);
/**
* 文件复制移动操作
*/
async function transfer(config) {
const { filePath, transferOperation, newPath } = config;
// 检查文件是否存在
try {
const stats = await fs.lstat(filePath);
// 确保目标目录存在
await fs.mkdir(path.dirname(newPath), { recursive: true });
if (transferOperation === "copy") {
const processBar = await quickcommand.showProcessBar({
text: "复制中...",
});
if (stats.isDirectory()) {
// 复制目录
const copyDir = async (src, dest) => {
await fs.mkdir(dest, { recursive: true });
const entries = await fs.readdir(src);
for (const entry of entries) {
const srcPath = path.join(src, entry);
const destPath = path.join(dest, entry);
const entryStat = await fs.lstat(srcPath);
if (entryStat.isDirectory()) {
await copyDir(srcPath, destPath);
} else {
await fs.copyFile(srcPath, destPath);
}
quickcommand.updateProcessBar({ text: entry }, processBar);
}
};
await copyDir(filePath, newPath);
} else {
// 复制文件
await fs.copyFile(filePath, newPath);
}
processBar.close();
} else if (transferOperation === "rename") {
const processBar = await quickcommand.showProcessBar({
text: "处理中...",
});
await fs.rename(filePath, newPath);
processBar.close();
} else {
throw new Error(`不支持的操作类型: ${transferOperation}`);
}
} catch (error) {
processBar?.close();
if (error.code === "ENOENT") {
throw new Error("文件或目录不存在");
}
throw error;
}
}
@ -246,69 +272,49 @@ async function list(config) {
}
}
/**
* 格式化文件大小
* @param {number} bytes 字节数
* @returns {string} 格式化后的文件大小
*/
function formatBytes(bytes) {
const units = ["B", "KB", "MB", "GB", "TB"];
let unitIndex = 0;
while (bytes >= 1024 && unitIndex < units.length - 1) {
bytes /= 1024;
unitIndex++;
}
return `${bytes.toFixed(2)} ${units[unitIndex]}`;
}
/**
* 获取文件或目录状态
* @param {Object} config 配置对象
* @param {string} config.filePath 路径
* @param {string} config.targetType 目标类型
* @param {string} config.statMode 检查类型
* @param {boolean} [config.followSymlinks] 是否跟随符号链接
* @returns {Promise<Object>} 状态信息
*/
async function stat(config) {
const { filePath, targetType, statMode, followSymlinks } = config;
const { filePath, followSymlinks } = config;
try {
const statFn = followSymlinks ? fs.stat : fs.lstat;
const stats = await statFn(filePath);
// 检查目标类型是否匹配
if (targetType === "file" && !stats.isFile()) {
throw new Error("目标不是文件");
}
if (targetType === "directory" && !stats.isDirectory()) {
throw new Error("目标不是目录");
}
// 根据检查类型返回不同的信息
if (statMode === "exists") {
return {
exists: true,
isFile: stats.isFile(),
isDirectory: stats.isDirectory(),
};
} else if (statMode === "status") {
return {
exists: true,
isFile: stats.isFile(),
isDirectory: stats.isDirectory(),
isSymbolicLink: stats.isSymbolicLink(),
size: stats.size,
mode: stats.mode,
uid: stats.uid,
gid: stats.gid,
accessTime: stats.atime,
modifyTime: stats.mtime,
changeTime: stats.ctime,
birthTime: stats.birthtime,
};
} else {
// 默认返回基本信息
return {
exists: true,
isFile: stats.isFile(),
isDirectory: stats.isDirectory(),
isSymbolicLink: stats.isSymbolicLink(),
};
}
return {
exists: true,
isFile: stats.isFile(),
isDirectory: stats.isDirectory(),
isSymbolicLink: stats.isSymbolicLink(),
humanReadSize: formatBytes(stats.size),
...stats,
};
} catch (error) {
if (error.code === "ENOENT") {
return {
exists: false,
...(statMode === "exists" && {
isFile: false,
isDirectory: false,
}),
isFile: false,
isDirectory: false,
};
}
throw error;
@ -319,39 +325,28 @@ async function stat(config) {
* 统一的文件操作入口
*/
async function operation(config) {
if (!config || typeof config !== "object") {
throw new Error("配置参数必须是一个对象");
}
const { operation: op } = config;
const { operation } = config;
if (!operation) {
throw new Error("缺少必要的 operation 参数");
}
switch (operation) {
switch (op) {
case "read":
return await read(config);
case "write":
return await write(config);
case "list":
return await list(config);
case "stat":
return await stat(config);
case "delete":
return await remove(config);
case "manage":
return await manage(config);
case "stat":
return await stat(config);
case "permission":
return await permission(config);
case "transfer":
return await transfer(config);
default:
throw new Error(`不支持的操作类型: ${operation}`);
throw new Error(`不支持的操作类型: ${op}`);
}
}
module.exports = {
read,
write,
list,
stat,
remove,
manage,
operation,
};

View File

@ -43,10 +43,16 @@
<div class="q-ml-xs">删除</div>
</div>
</q-tab>
<q-tab name="manage" no-caps>
<q-tab name="transfer" no-caps>
<div class="row items-center no-wrap">
<q-icon name="settings" size="16px" />
<div class="q-ml-xs">管理</div>
<q-icon name="drive_file_move" size="16px" />
<div class="q-ml-xs">复制移动</div>
</div>
</q-tab>
<q-tab name="permission" no-caps>
<div class="row items-center no-wrap">
<q-icon name="security" size="16px" />
<div class="q-ml-xs">权限</div>
</div>
</q-tab>
</q-tabs>
@ -64,10 +70,7 @@
dialog: {
options: {
title: '选择文件',
properties: [
shouldSelectDirectory ? 'openDirectory' : 'openFile',
'showHiddenFiles',
],
properties: ['openDirectory', 'openFile', 'showHiddenFiles'],
},
},
}"
@ -100,25 +103,13 @@
map-options
@update:model-value="updateArgvs('readMode', $event)"
/>
<q-select
v-model="argvs.readFlag"
:options="readFlagOptions"
label="读取标志"
dense
options-dense
filled
class="col-grow"
emit-value
map-options
@update:model-value="updateArgvs('readFlag', $event)"
/>
<NumberInput
v-if="argvs.readMode === 'start'"
:model-value="argvs.start"
@update:model-value="updateArgvs('start', $event)"
label="起始位置"
icon="first_page"
class="col-grow"
class="col"
/>
<NumberInput
v-if="argvs.readMode === 'start'"
@ -126,7 +117,7 @@
@update:model-value="updateArgvs('length', $event)"
label="读取长度"
icon="last_page"
class="col-grow"
class="col"
/>
</div>
</template>
@ -186,7 +177,7 @@
@update:model-value="updateArgvs('content', $event)"
label="写入内容"
icon="edit"
class="col-12"
class="col-grow"
/>
</div>
</template>
@ -194,80 +185,87 @@
<!-- 删除操作配置 -->
<template v-if="argvs.operation === 'delete'">
<div class="row q-gutter-sm">
<q-select
v-model="argvs.targetType"
:options="targetTypeOptions"
label="目标类型"
dense
filled
options-dense
class="col-grow"
emit-value
map-options
@update:model-value="updateArgvs('targetType', $event)"
/>
<q-checkbox
<CheckButton
v-model="argvs.recursive"
label="递归删除"
v-if="argvs.targetType === 'directory'"
dense
class="col-grow"
class="col"
@update:model-value="updateArgvs('recursive', $event)"
/>
<q-checkbox
<CheckButton
v-model="argvs.force"
label="强制删除"
dense
class="col-grow"
class="col"
@update:model-value="updateArgvs('force', $event)"
/>
</div>
</template>
<!-- 管理操作配置 -->
<template v-if="argvs.operation === 'manage'">
<!-- 列目录操作配置 -->
<template v-if="argvs.operation === 'list'">
<div class="row q-gutter-sm">
<q-select
v-model="argvs.targetType"
:options="targetTypeOptions"
label="目标类型"
dense
options-dense
filled
class="col-grow"
emit-value
map-options
@update:model-value="updateArgvs('targetType', $event)"
<CheckButton
v-model="argvs.recursive"
label="递归列出子目录"
class="col"
@update:model-value="updateArgvs('recursive', $event)"
/>
<q-select
v-model="argvs.manageOperation"
:options="manageOperationOptions"
label="管理操作"
dense
options-dense
filled
class="col-grow"
emit-value
map-options
@update:model-value="updateArgvs('manageOperation', $event)"
<CheckButton
v-model="argvs.showHidden"
label="显示隐藏文件"
class="col"
@update:model-value="updateArgvs('showHidden', $event)"
/>
</div>
</template>
<!-- 重命名操作 -->
<template v-if="argvs.manageOperation === 'rename'">
<div class="row q-gutter-sm">
<!-- 状态操作配置 -->
<template v-if="argvs.operation === 'stat'">
<div class="row q-gutter-sm">
<CheckButton
v-model="argvs.followSymlinks"
label="跟随符号链接"
@update:model-value="updateArgvs('followSymlinks', $event)"
class="col-grow"
/>
</div>
</template>
<!-- 复制移动操作配置 -->
<template v-if="argvs.operation === 'transfer'">
<div class="row q-gutter-sm">
<div class="col-6">
<VariableInput
:model-value="argvs.newPath"
@update:model-value="updateArgvs('newPath', $event)"
label="新路径"
label="目标路径"
icon="drive_file_rename_outline"
class="col-grow"
class="col-6"
/>
</div>
</template>
<div class="col">
<ButtonGroup
v-model="argvs.transferOperation"
:options="transferOperationOptions"
height="36px"
@update:model-value="updateArgvs('transferOperation', $event)"
/>
</div>
</div>
</template>
<!-- 权限操作配置 -->
<template v-if="argvs.operation === 'permission'">
<div class="row q-gutter-sm">
<ButtonGroup
v-model="argvs.operationType"
class="col"
:options="operationTypeOptions"
@update:model-value="updateArgvs('operationType', $event)"
/>
</div>
<!-- 修改权限操作 -->
<template v-if="argvs.manageOperation === 'chmod'">
<template v-if="argvs.operationType === 'chmod'">
<div class="row q-gutter-sm">
<q-select
v-model="argvs.mode"
@ -290,11 +288,9 @@
</q-item>
</template>
</q-select>
<q-checkbox
<CheckButton
v-model="argvs.recursive"
label="递归修改"
v-if="argvs.targetType === 'directory'"
dense
class="col-grow"
@update:model-value="updateArgvs('recursive', $event)"
/>
@ -302,7 +298,7 @@
</template>
<!-- 修改所有者操作 -->
<template v-if="argvs.manageOperation === 'chown'">
<template v-if="argvs.operationType === 'chown'">
<div class="row q-gutter-sm">
<NumberInput
:model-value="argvs.uid"
@ -318,75 +314,15 @@
icon="group"
class="col-grow"
/>
<q-checkbox
<CheckButton
v-model="argvs.recursive"
label="递归修改"
v-if="argvs.targetType === 'directory'"
dense
class="col-grow"
@update:model-value="updateArgvs('recursive', $event)"
/>
</div>
</template>
</template>
<!-- 列目录操作配置 -->
<template v-if="argvs.operation === 'list'">
<div class="row q-gutter-sm q-px-xs">
<q-checkbox
v-model="argvs.recursive"
label="递归列出子目录"
dense
class="col-grow"
@update:model-value="updateArgvs('recursive', $event)"
/>
<q-checkbox
v-model="argvs.showHidden"
label="显示隐藏文件"
dense
class="col-grow"
@update:model-value="updateArgvs('showHidden', $event)"
/>
</div>
</template>
<!-- 状态操作配置 -->
<template v-if="argvs.operation === 'stat'">
<div class="row q-gutter-sm">
<q-select
v-model="argvs.targetType"
:options="targetTypeOptions"
label="目标类型"
dense
filled
options-dense
class="col-grow"
emit-value
map-options
@update:model-value="updateArgvs('targetType', $event)"
/>
<q-select
v-model="argvs.statMode"
:options="statModeOptions"
label="检查类型"
dense
filled
options-dense
class="col-grow"
emit-value
map-options
@update:model-value="updateArgvs('statMode', $event)"
/>
<q-checkbox
v-model="argvs.followSymlinks"
label="跟随符号链接"
v-if="argvs.statMode === 'status'"
@update:model-value="updateArgvs('followSymlinks', $event)"
dense
class="col-grow"
/>
</div>
</template>
</div>
</template>
@ -394,7 +330,9 @@
import { defineComponent } from "vue";
import VariableInput from "components/composer/common/VariableInput.vue";
import NumberInput from "components/composer/common/NumberInput.vue";
import { stringifyArgv, parseFunction } from "js/composer/formatString";
import ButtonGroup from "components/composer/common/ButtonGroup.vue";
import CheckButton from "components/composer/common/CheckButton.vue";
import { stringifyArgv } from "js/composer/formatString";
import { newVarInputVal } from "js/composer/varInputValManager";
//
@ -418,11 +356,6 @@ const READ_MODE_OPTIONS = [
{ label: "按行读取", value: "line" },
];
const READ_FLAG_OPTIONS = [
{ label: "同步读取", value: "sync" },
{ label: "异步读取", value: "async" },
];
const WRITE_MODE_OPTIONS = [
{ label: "覆盖写入", value: "write" },
{ label: "追加写入", value: "append" },
@ -435,20 +368,19 @@ const WRITE_FLAG_OPTIONS = [
{ label: "777", value: "777", hint: "所有人读写执行" },
];
const STAT_MODE_OPTIONS = [
{ label: "检查存在", value: "exists" },
{ label: "完整状态", value: "status" },
];
const TARGET_TYPE_OPTIONS = [
{ label: "文件", value: "file" },
{ label: "目录", value: "directory" },
];
const MANAGE_OPERATION_OPTIONS = [
{ label: "重命名", value: "rename" },
{ label: "修改权限", value: "chmod" },
{ label: "修改所有者", value: "chown" },
const OPERATION_TYPE_OPTIONS = [
{ label: "修改权限", value: "chmod", icon: "lock" },
{ label: "修改所有者", value: "chown", icon: "person" },
];
const TRANSFER_OPERATION_OPTIONS = [
{ label: "移动/重命名", value: "rename", icon: "drive_file_rename_outline" },
{ label: "复制", value: "copy", icon: "content_copy" },
];
export default defineComponent({
@ -456,6 +388,8 @@ export default defineComponent({
components: {
VariableInput,
NumberInput,
ButtonGroup,
CheckButton,
},
props: {
modelValue: {
@ -468,44 +402,34 @@ export default defineComponent({
return {
encodingOptions: ENCODING_OPTIONS,
readModeOptions: READ_MODE_OPTIONS,
readFlagOptions: READ_FLAG_OPTIONS,
writeModeOptions: WRITE_MODE_OPTIONS,
writeFlagOptions: WRITE_FLAG_OPTIONS,
statModeOptions: STAT_MODE_OPTIONS,
targetTypeOptions: TARGET_TYPE_OPTIONS,
manageOperationOptions: MANAGE_OPERATION_OPTIONS,
operationTypeOptions: OPERATION_TYPE_OPTIONS,
transferOperationOptions: TRANSFER_OPERATION_OPTIONS,
defaultArgvs: {
operation: "read",
filePath: newVarInputVal("str"),
encoding: "utf8",
readMode: "all",
readFlag: "async",
start: 0,
length: 100,
targetType: "file",
writeMode: "write",
writeFlag: "644",
statMode: "exists",
followSymlinks: false,
followSymlinks: true,
mode: "644",
recursive: false,
force: false,
showHidden: false,
manageOperation: "rename",
operationType: "chmod",
transferOperation: "rename",
},
};
},
computed: {
argvs() {
return (
this.modelValue.argvs || this.parseCodeToArgvs(this.modelValue.code)
);
},
shouldSelectDirectory() {
return (
this.argvs.operation === "list" ||
(this.argvs.targetType === "directory" &&
["delete", "manage", "stat"].includes(this.argvs.operation))
);
return this.modelValue.argvs || this.defaultArgvs;
},
},
methods: {
@ -519,7 +443,6 @@ export default defineComponent({
switch (argvs.operation) {
case "read":
params.encoding = argvs.encoding;
params.readFlag = argvs.readFlag;
params.readMode = argvs.readMode;
if (argvs.readMode === "start") {
params.start = Number(argvs.start) || 0;
@ -537,32 +460,33 @@ export default defineComponent({
case "list":
params.targetType = "directory";
params.recursive = argvs.recursive;
params.showHidden = argvs.showHidden;
break;
case "delete":
params.targetType = argvs.targetType;
params.force = argvs.force;
params.recursive =
argvs.recursive && argvs.targetType === "directory";
break;
case "manage":
params.targetType = argvs.targetType;
params.manageOperation = argvs.manageOperation;
if (argvs.manageOperation === "rename") {
params.newPath = argvs.newPath;
} else {
if (argvs.mode) params.mode = argvs.mode;
if (argvs.uid) params.uid = argvs.uid;
if (argvs.gid) params.gid = argvs.gid;
}
params.recursive = argvs.recursive;
break;
case "stat":
params.targetType = argvs.targetType;
params.statMode = argvs.statMode;
params.followSymlinks = argvs.followSymlinks;
break;
case "permission":
params.operationType = argvs.operationType;
if (argvs.operationType === "chmod") {
params.mode = argvs.mode;
} else {
params.uid = argvs.uid;
params.gid = argvs.gid;
}
params.recursive = argvs.recursive;
break;
case "transfer":
params.transferOperation = argvs.transferOperation;
params.newPath = argvs.newPath;
break;
}
return `${this.modelValue.value}(${stringifyArgv(params)})`;
@ -610,52 +534,6 @@ export default defineComponent({
this.argvs.mode = `${ownerValue}${groupValue}${otherValue}`;
this.updateArgvs();
},
parseCodeToArgvs(code) {
const argvs = window.lodashM.cloneDeep(this.defaultArgvs);
if (!code) return argvs;
try {
const variableFormatPaths = [
"arg0.filePath",
"arg0.content",
"arg0.newPath",
];
const result = parseFunction(code, { variableFormatPaths });
let params = result.argvs[0];
//
switch (params.operation) {
case "read":
if (params.readMode === "start") {
params.start = Number(params.start) || 0;
params.length = Number(params.length) || 100;
}
break;
case "write":
// flag writeMode
params.writeMode = params.flag === "a" ? "append" : "write";
// mode writeFlag
params.writeFlag = params.mode;
break;
case "list":
params.targetType = "directory";
break;
case "delete":
case "manage":
case "stat":
// 使
break;
}
return params;
} catch (e) {
console.error("解析文件操作参数失败:", e);
}
return argvs;
},
updateModelValue(argvs) {
this.$emit("update:modelValue", {
...this.modelValue,
@ -670,19 +548,11 @@ export default defineComponent({
write: "写入",
list: "列目录",
delete: "删除",
manage: "管理",
stat: "状态",
stat: "获取状态",
transfer: "复制移动",
permission: "设置权限",
};
const findOptionsLabel = (options, value) => {
return options.find((option) => option.value === value)?.label || value;
};
let operationInfo =
argvs.operation === "manage"
? findOptionsLabel(MANAGE_OPERATION_OPTIONS, argvs.manageOperation) +
" "
: argvs.operation === "stat"
? findOptionsLabel(STAT_MODE_OPTIONS, argvs.statMode) + " "
: operationDict[argvs.operation] + " ";
let operationInfo = operationDict[argvs.operation] + " ";
return operationInfo + argvs.filePath.value;
},
},

View File

@ -382,7 +382,7 @@ export default defineComponent({
},
computed: {
argvs() {
return this.modelValue.argvs;
return this.modelValue.argvs || this.defaultArgvs;
},
hasRequestData() {
return ["PUT", "POST", "PATCH"].includes(this.argvs.method);

View File

@ -507,89 +507,58 @@ interface quickcomposerApi {
file: {
/**
*
* @param config
*/
operation: {
/**
*
* @param config
*/
operation(config: {
/** 操作类型read-读取write-写入list-列表stat-状态delete-删除manage-管理 */
operation: "read" | "write" | "list" | "stat" | "delete" | "manage";
/** 文件路径 */
filePath: string;
/** 读取操作配置 */
readOptions?: {
/** 编码方式 */
encoding?: BufferEncoding;
/** 起始位置 */
start?: number;
/** 结束位置 */
end?: number;
};
/** 写入操作配置 */
writeOptions?: {
/** 写入内容 */
content: string;
/** 编码方式 */
encoding?: BufferEncoding;
/** 写入模式overwrite-覆盖append-追加 */
writeMode?: "overwrite" | "append";
/** 文件权限 */
writeFlag?: string;
};
/** 列表操作配置 */
listOptions?: {
/** 是否递归 */
recursive?: boolean;
/** 是否包含隐藏文件 */
includeHidden?: boolean;
};
/** 管理操作配置 */
manageOptions?: {
/** 目标类型file-文件directory-目录 */
targetType?: "file" | "directory";
/** 管理操作类型rename-重命名chmod-修改权限chown-修改所有者 */
manageOperation?: "rename" | "chmod" | "chown";
/** 新路径(重命名时使用) */
newPath?: string;
/** 权限模式(chmod时使用) */
mode?: string | number;
/** 用户ID(chown时使用) */
uid?: number;
/** 组ID(chown时使用) */
gid?: number;
/** 是否递归操作 */
recursive?: boolean;
};
}): Promise<any>;
};
operation(config: {
/** 操作类型read-读取write-写入list-列表stat-状态delete-删除permission-权限transfer-复制移动 */
operation:
| "read"
| "write"
| "list"
| "stat"
| "delete"
| "permission"
| "transfer";
/** 文件路径 */
filePath: string;
/** 读取操作配置 */
encoding?: BufferEncoding;
readMode?: "all" | "start" | "line";
start?: number;
length?: number;
/** 写入操作配置 */
content?: string;
flag?: "w" | "a";
mode?: string | number;
/** 列表操作配置 */
recursive?: boolean;
showHidden?: boolean;
/** 权限操作配置 */
operationType?: "chmod" | "chown";
uid?: number;
gid?: number;
/** 复制移动操作配置 */
transferOperation?: "copy" | "rename";
newPath?: string;
/** 删除操作配置 */
force?: boolean;
/** 状态操作配置 */
followSymlinks?: boolean;
}): Promise<any>;
/**
*
*
* @param operation compress-extract-
* @param format zip, tar, gzip
* @param source /
* @param destination
*/
archive: {
/**
*
* @param config
*/
archive(config: {
/** 操作类型compress-压缩extract-解压 */
operation: "compress" | "extract";
/** 归档格式zip, tar, gzip */
format: "zip" | "tar" | "gzip";
/** 源文件/文件夹路径 */
sourcePath: string | string[];
/** 目标路径 */
targetPath: string;
/** 压缩级别(1-9) */
level?: number;
/** 是否保持目录结构 */
preserveStructure?: boolean;
/** 密码保护(仅zip格式支持) */
password?: string;
}): Promise<void>;
};
archive(
operation: "compress" | "extract",
format: "zip" | "tar" | "gzip",
source: string | string[],
destination: string
): Promise<void>;
};
system: {
/**