diff --git a/backend/app/api/v1/file.go b/backend/app/api/v1/file.go index ad178dca6..207277920 100644 --- a/backend/app/api/v1/file.go +++ b/backend/app/api/v1/file.go @@ -207,3 +207,17 @@ func (b *BaseApi) Download(c *gin.Context) { } c.File(filePath) } + +func (b *BaseApi) Size(c *gin.Context) { + var req dto.DirSizeReq + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + res, err := fileService.DirSize(req) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, res) +} diff --git a/backend/app/dto/file.go b/backend/app/dto/file.go index 9c2f1e5a9..9d3afeb78 100644 --- a/backend/app/dto/file.go +++ b/backend/app/dto/file.go @@ -75,3 +75,11 @@ type FileDownload struct { Type string `json:"type" validate:"required"` Name string `json:"name" validate:"required"` } + +type DirSizeReq struct { + Path string `json:"path" validate:"required"` +} + +type DirSizeRes struct { + Size float64 `json:"size" validate:"required"` +} diff --git a/backend/app/service/file.go b/backend/app/service/file.go index 1f6007766..a5a540e45 100644 --- a/backend/app/service/file.go +++ b/backend/app/service/file.go @@ -167,6 +167,15 @@ func (f FileService) FileDownload(d dto.FileDownload) (string, error) { return filePath, nil } +func (f FileService) DirSize(req dto.DirSizeReq) (dto.DirSizeRes, error) { + fo := files.NewFileOp() + size, err := fo.GetDirSize(req.Path) + if err != nil { + return dto.DirSizeRes{}, err + } + return dto.DirSizeRes{Size: size}, nil +} + func getUuid() string { b := make([]byte, 16) _, _ = io.ReadFull(rand.Reader, b) diff --git a/backend/router/ro_file.go b/backend/router/ro_file.go index f88c19470..60d94c168 100644 --- a/backend/router/ro_file.go +++ b/backend/router/ro_file.go @@ -29,6 +29,7 @@ func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) { fileRouter.POST("/wget", baseApi.WgetFile) fileRouter.POST("/move", baseApi.MoveFile) fileRouter.POST("/download", baseApi.Download) + fileRouter.POST("/size", baseApi.Size) } } diff --git a/backend/utils/files/file_op.go b/backend/utils/files/file_op.go index 18905e709..90decb1c8 100644 --- a/backend/utils/files/file_op.go +++ b/backend/utils/files/file_op.go @@ -12,6 +12,7 @@ import ( "os" "path" "path/filepath" + "sync" ) type FileOp struct { @@ -221,6 +222,23 @@ func (f FileOp) CopyFile(src, dst string) error { return nil } +func (f FileOp) GetDirSize(path string) (float64, error) { + var m sync.Map + var wg sync.WaitGroup + + wg.Add(1) + go ScanDir(f.Fs, path, &m, &wg) + wg.Wait() + + var dirSize float64 + m.Range(func(k, v interface{}) bool { + dirSize = dirSize + v.(float64) + return true + }) + + return dirSize, nil +} + type CompressType string const ( diff --git a/backend/utils/files/utils.go b/backend/utils/files/utils.go index e94a2c330..717c11457 100644 --- a/backend/utils/files/utils.go +++ b/backend/utils/files/utils.go @@ -2,9 +2,12 @@ package files import ( "github.com/gabriel-vasile/mimetype" + "github.com/spf13/afero" "os" "os/user" + "path/filepath" "strconv" + "sync" ) func IsSymlink(mode os.FileMode) bool { @@ -43,6 +46,22 @@ func GetSymlink(path string) string { return linkPath } +func ScanDir(fs afero.Fs, path string, dirMap *sync.Map, wg *sync.WaitGroup) { + afs := &afero.Afero{Fs: fs} + files, _ := afs.ReadDir(path) + for _, f := range files { + if f.IsDir() { + wg.Add(1) + go ScanDir(fs, filepath.Join(path, f.Name()), dirMap, wg) + } else { + if f.Size() > 0 { + dirMap.Store(filepath.Join(path, f.Name()), float64(f.Size())) + } + } + } + defer wg.Done() +} + const dotCharacter = 46 func IsHidden(path string) bool { diff --git a/frontend/src/api/interface/file.ts b/frontend/src/api/interface/file.ts index 3e01d2b0a..200954f08 100644 --- a/frontend/src/api/interface/file.ts +++ b/frontend/src/api/interface/file.ts @@ -15,6 +15,7 @@ export namespace File { modTime: string; mode: number; mimeType: string; + dirSize: number; items: File[]; } @@ -89,4 +90,12 @@ export namespace File { name: string; url: string; } + + export interface DirSizeReq { + path: string; + } + + export interface DirSizeRes { + size: number; + } } diff --git a/frontend/src/api/modules/files.ts b/frontend/src/api/modules/files.ts index c57c2a6d6..bc78ef289 100644 --- a/frontend/src/api/modules/files.ts +++ b/frontend/src/api/modules/files.ts @@ -56,3 +56,7 @@ export const MoveFile = (params: File.FileMove) => { export const DownloadFile = (params: File.FileDownload) => { return http.download('files/download', params, { responseType: 'blob' }); }; + +export const ComputeDirSize = (params: File.DirSizeReq) => { + return http.post('files/size', params); +}; diff --git a/frontend/src/components/app-layout/index.vue b/frontend/src/components/app-layout/index.vue index f0efeb73b..7ca024442 100644 --- a/frontend/src/components/app-layout/index.vue +++ b/frontend/src/components/app-layout/index.vue @@ -3,9 +3,9 @@ - + @@ -13,7 +13,7 @@ diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 56fae3318..e33dfbf94 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -242,5 +242,6 @@ export default { moveStart: 'Move start', move: 'Move', copy: 'Cpoy', + calculate: 'Calculate', }, }; diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 93233fc54..1b668b302 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -242,5 +242,6 @@ export default { moveStart: '移动成功', move: '移动', copy: '复制', + calculate: '计算', }, }; diff --git a/frontend/src/utils/util.ts b/frontend/src/utils/util.ts index 5f9f6b04f..108af921f 100644 --- a/frontend/src/utils/util.ts +++ b/frontend/src/utils/util.ts @@ -72,3 +72,13 @@ export function getRandomStr(e: number): string { } return n; } + +export function computeSize(size: number): string { + const num = 1024.0; + + if (size < num) return size + ' B'; + if (size < Math.pow(num, 2)) return (size / num).toFixed(2) + ' K'; + if (size < Math.pow(num, 3)) return (size / Math.pow(num, 2)).toFixed(2) + ' MB'; + if (size < Math.pow(num, 4)) return (size / Math.pow(num, 3)).toFixed(2) + ' GB'; + return (size / Math.pow(num, 4)).toFixed(2) + ' TB'; +} diff --git a/frontend/src/views/host/file-management/index.vue b/frontend/src/views/host/file-management/index.vue index 5a6cc5755..66273a136 100644 --- a/frontend/src/views/host/file-management/index.vue +++ b/frontend/src/views/host/file-management/index.vue @@ -105,7 +105,19 @@ - + + + import { onMounted, reactive, ref } from '@vue/runtime-core'; -import { GetFilesList, GetFilesTree, DeleteFile, GetFileContent, SaveFileContent } from '@/api/modules/files'; -import { dateFromat, getRandomStr } from '@/utils/util'; +import { + GetFilesList, + GetFilesTree, + DeleteFile, + GetFileContent, + SaveFileContent, + ComputeDirSize, +} from '@/api/modules/files'; +import { computeSize, dateFromat, getRandomStr } from '@/utils/util'; import { File } from '@/api/interface/file'; import { useDeleteData } from '@/hooks/use-delete-data'; import { ElMessage } from 'element-plus'; @@ -192,10 +211,10 @@ import Move from './move/index.vue'; import Download from './download/index.vue'; const data = ref(); -const selects = ref([]); -const req = reactive({ path: '/', expand: true, showHidden: false }); -const loading = ref(false); -const treeLoading = ref(false); +let selects = ref([]); +let req = reactive({ path: '/', expand: true, showHidden: false }); +let loading = ref(false); +let treeLoading = ref(false); const paths = ref([]); const fileTree = ref([]); const expandKeys = ref([]); @@ -326,6 +345,24 @@ const delFile = async (row: File.File | null) => { search(req); }; +const getFileSize = (size: number) => { + return computeSize(size); +}; + +const getDirSize = async (row: any) => { + const req = { + path: row.path, + }; + loading.value = true; + await ComputeDirSize(req) + .then(async (res) => { + row.dirSize = res.data.size; + }) + .finally(() => { + loading.value = false; + }); +}; + const closeCreate = () => { filePage.open = false; search(req);