feat: 增加创建软链接文件功能

This commit is contained in:
zhengkunwang223
2022-08-31 16:00:51 +08:00
parent 245f5fb68a
commit 1b0660f4a3
9 changed files with 98 additions and 27 deletions

View File

@@ -20,10 +20,13 @@ type FileTree struct {
} }
type FileCreate struct { type FileCreate struct {
Path string Path string
Content string Content string
IsDir bool IsDir bool
Mode int64 Mode int64
IsLink bool
IsSymlink bool
LinkPath string
} }
type FileDelete struct { type FileDelete struct {

View File

@@ -50,16 +50,18 @@ func (f FileService) GetFileTree(op dto.FileOption) ([]dto.FileTree, error) {
func (f FileService) Create(op dto.FileCreate) error { func (f FileService) Create(op dto.FileCreate) error {
fo := files.NewFileOp() fo := files.NewFileOp()
if fo.Stat(op.Path) { if fo.Stat(op.Path) {
return errors.New("file is exist") return errors.New("file is exist")
} }
if op.IsDir { if op.IsDir {
return fo.CreateDir(op.Path, fs.FileMode(op.Mode)) return fo.CreateDir(op.Path, fs.FileMode(op.Mode))
} else {
if op.IsLink {
return fo.LinkFile(op.LinkPath, op.Path, op.IsSymlink)
} else {
return fo.CreateFile(op.Path)
}
} }
return nil
} }
func (f FileService) Delete(op dto.FileDelete) error { func (f FileService) Delete(op dto.FileDelete) error {

View File

@@ -25,6 +25,22 @@ func (f FileOp) CreateDir(dst string, mode fs.FileMode) error {
return f.Fs.MkdirAll(dst, mode) return f.Fs.MkdirAll(dst, mode)
} }
func (f FileOp) CreateFile(dst string) error {
if _, err := f.Fs.Create(dst); err != nil {
return err
}
return nil
}
func (f FileOp) LinkFile(source string, dst string, isSymlink bool) error {
if isSymlink {
osFs := afero.OsFs{}
return osFs.SymlinkIfPossible(source, dst)
} else {
return os.Link(source, dst)
}
}
func (f FileOp) DeleteDir(dst string) error { func (f FileOp) DeleteDir(dst string) error {
return f.Fs.RemoveAll(dst) return f.Fs.RemoveAll(dst)
} }

View File

@@ -21,6 +21,7 @@ type FileInfo struct {
Size int64 `json:"size"` Size int64 `json:"size"`
IsDir bool `json:"isDir"` IsDir bool `json:"isDir"`
IsSymlink bool `json:"isSymlink"` IsSymlink bool `json:"isSymlink"`
LinkPath string `json:"linkPath"`
Type string `json:"type"` Type string `json:"type"`
Mode string `json:"mode"` Mode string `json:"mode"`
MimeType string `json:"mimeType"` MimeType string `json:"mimeType"`
@@ -59,6 +60,9 @@ func NewFileInfo(op FileOption) (*FileInfo, error) {
Group: GetGroup(info.Sys().(*syscall.Stat_t).Gid), Group: GetGroup(info.Sys().(*syscall.Stat_t).Gid),
MimeType: GetMimeType(op.Path), MimeType: GetMimeType(op.Path),
} }
if file.IsSymlink {
file.LinkPath = GetSymlink(op.Path)
}
if op.Expand { if op.Expand {
if file.IsDir { if file.IsDir {
if err := file.listChildren(); err != nil { if err := file.listChildren(); err != nil {
@@ -111,6 +115,9 @@ func (f *FileInfo) listChildren() error {
Group: GetGroup(df.Sys().(*syscall.Stat_t).Gid), Group: GetGroup(df.Sys().(*syscall.Stat_t).Gid),
MimeType: GetMimeType(fPath), MimeType: GetMimeType(fPath),
} }
if isSymlink {
file.LinkPath = GetSymlink(fPath)
}
if isInvalidLink { if isInvalidLink {
file.Type = "invalid_link" file.Type = "invalid_link"

View File

@@ -34,3 +34,11 @@ func GetMimeType(path string) string {
} }
return mime.String() return mime.String()
} }
func GetSymlink(path string) string {
linkPath, err := os.Readlink(path)
if err != nil {
return ""
}
return linkPath
}

View File

@@ -9,6 +9,7 @@ export namespace File {
size: number; size: number;
isDir: boolean; isDir: boolean;
isSymlink: boolean; isSymlink: boolean;
linkPath: boolean;
type: string; type: string;
updateTime: string; updateTime: string;
modTime: string; modTime: string;
@@ -35,6 +36,9 @@ export namespace File {
path: string; path: string;
isDir: boolean; isDir: boolean;
mode: number; mode: number;
isLink?: boolean;
isSymlink?: boolean;
linkPath?: boolean;
} }
export interface FileDelete { export interface FileDelete {

View File

@@ -192,5 +192,9 @@ export default {
compressSuccess: '压缩成功', compressSuccess: '压缩成功',
deCompressSuccess: '解压成功', deCompressSuccess: '解压成功',
deCompressDst: '解压路径', deCompressDst: '解压路径',
linkType: '链接类型',
softLink: '软链接',
hardLink: '硬链接',
linkPath: '链接路径',
}, },
}; };

View File

@@ -7,13 +7,29 @@
@open="onOpen" @open="onOpen"
v-loading="loading" v-loading="loading"
> >
<el-form ref="fileForm" label-position="left" :model="form" label-width="100px" :rules="rules"> <el-form ref="fileForm" label-position="left" :model="addForm" label-width="100px" :rules="rules">
<el-form-item :label="$t('file.path')" prop="path"> <el-input v-model="getPath" disabled /></el-form-item> <el-form-item :label="$t('file.path')" prop="path"> <el-input v-model="getPath" disabled /></el-form-item>
<el-form-item :label="$t('file.name')" prop="name"> <el-input v-model="form.name" /></el-form-item> <el-form-item :label="$t('file.name')" prop="name"> <el-input v-model="addForm.name" /></el-form-item>
<el-checkbox v-model="isLink" :label="$t('file.link')"></el-checkbox> <el-form-item v-if="!addForm.isDir">
<el-checkbox v-model="addForm.isLink" :label="$t('file.link')"></el-checkbox
></el-form-item>
<el-form-item :label="$t('file.linkType')" v-if="addForm.isLink" prop="linkType">
<el-radio-group v-model="addForm.isSymlink">
<el-radio :label="true">{{ $t('file.softLink') }}</el-radio>
<el-radio :label="false">{{ $t('file.hardLink') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="addForm.isLink" :label="$t('file.linkPath')" prop="linkPath">
<el-input v-model="addForm.linkPath"
/></el-form-item>
<el-form-item>
<el-checkbox v-if="addForm.isDir" v-model="setRole" :label="$t('file.setRole')"></el-checkbox>
</el-form-item>
<el-form-item>
<FileRole v-if="setRole" :mode="'0755'" @get-mode="getMode"></FileRole>
</el-form-item>
</el-form> </el-form>
<el-checkbox v-model="setRole" :label="$t('file.setRole')"></el-checkbox>
<FileRole v-if="setRole" :mode="'0755'" @get-mode="getMode"></FileRole>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button> <el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button>
@@ -35,15 +51,15 @@ import { Rules } from '@/global/form-rues';
const fileForm = ref<FormInstance>(); const fileForm = ref<FormInstance>();
let loading = ref(false); let loading = ref(false);
let setRole = ref(false); let setRole = ref(false);
let isLink = ref(false);
const props = defineProps({ const props = defineProps({
open: Boolean, open: Boolean,
file: Object, file: Object,
}); });
const { open, file } = toRefs(props); const { open, file } = toRefs(props);
let addItem = ref<File.FileCreate>({ path: '', isDir: false, mode: 0o755 });
let form = ref({ name: '', path: '' }); let addForm = reactive({ path: '', name: '', isDir: false, mode: 0o755, isLink: false, isSymlink: true, linkPath: '' });
const em = defineEmits(['close']); const em = defineEmits(['close']);
const handleClose = () => { const handleClose = () => {
em('close', open); em('close', open);
@@ -52,17 +68,19 @@ const handleClose = () => {
const rules = reactive<FormRules>({ const rules = reactive<FormRules>({
name: [Rules.required], name: [Rules.required],
path: [Rules.required], path: [Rules.required],
isSymlink: [Rules.required],
linkPath: [Rules.required],
}); });
const getMode = (val: number) => { const getMode = (val: number) => {
addItem.value.mode = val; addForm.mode = val;
}; };
let getPath = computed(() => { let getPath = computed(() => {
if (form.value.path === '/') { if (addForm.path === '/') {
return form.value.path + form.value.name; return addForm.path + addForm.name;
} else { } else {
return form.value.path + '/' + form.value.name; return addForm.path + '/' + addForm.name;
} }
}); });
@@ -72,9 +90,12 @@ const submit = async (formEl: FormInstance | undefined) => {
if (!valid) { if (!valid) {
return; return;
} }
let addItem = {};
Object.assign(addItem, addForm);
addItem['path'] = getPath.value;
loading.value = true; loading.value = true;
addItem.value.path = getPath.value; CreateFile(addItem as File.FileCreate)
CreateFile(addItem.value)
.then(() => { .then(() => {
ElMessage.success(i18n.global.t('commons.msg.createSuccess')); ElMessage.success(i18n.global.t('commons.msg.createSuccess'));
handleClose(); handleClose();
@@ -87,9 +108,14 @@ const submit = async (formEl: FormInstance | undefined) => {
const onOpen = () => { const onOpen = () => {
const f = file?.value as File.FileCreate; const f = file?.value as File.FileCreate;
addItem.value.isDir = f.isDir; addForm.isDir = f.isDir;
addItem.value.path = f.path; addForm.path = f.path;
form.value.name = ''; addForm.name = '';
form.value.path = f.path; addForm.isLink = false;
init();
};
const init = () => {
setRole.value = false;
}; };
</script> </script>

View File

@@ -64,11 +64,12 @@
<el-button type="primary" plain> {{ $t('file.terminal') }}</el-button> <el-button type="primary" plain> {{ $t('file.terminal') }}</el-button>
<el-button type="primary" plain> {{ $t('file.shareList') }}</el-button> <el-button type="primary" plain> {{ $t('file.shareList') }}</el-button>
</template> </template>
<el-table-column :label="$t('commons.table.name')" min-width="150" fix> <el-table-column :label="$t('commons.table.name')" min-width="250" fix show-overflow-tooltip>
<template #default="{ row }"> <template #default="{ row }">
<svg-icon v-if="row.isDir" className="table-icon" iconName="p-file-folder"></svg-icon> <svg-icon v-if="row.isDir" className="table-icon" iconName="p-file-folder"></svg-icon>
<svg-icon v-else className="table-icon" iconName="p-file-normal"></svg-icon> <svg-icon v-else className="table-icon" iconName="p-file-normal"></svg-icon>
<el-link :underline="false" @click="open(row)">{{ row.name }}</el-link> <el-link :underline="false" @click="open(row)">{{ row.name }}</el-link>
<span v-if="row.isSymlink"> -> {{ row.linkPath }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('file.mode')" prop="mode"> <el-table-column :label="$t('file.mode')" prop="mode">