refactor: 全面重构代码并加白话注释,移动全局并行,去掉去向/重复记录
- 重构后端与前端,全程大白话注释(不懂程序也能看懂) - 移动阶段改为全局并行(MoveAll,工人数=CPU核心数) - 去掉记录每个文件搬去哪了(删除去向回填,解决写库卡顿) - 去掉重复文件标记与统计:重复判定改为 MD5+路径都相同才算,重复则原地忽略不移动;MD5 相同但路径不同仍照常搬 - 媒体库去掉归类目录列,看板去掉重复/归类分布统计
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
// Package api 提供服务器文件系统浏览接口,供前端目录选择器使用(只列出目录,不列文件)。
|
||||
// 文件目录浏览接口:给页面上"选择目录"用的,只列目录、不列文件。
|
||||
// 前端那个"选择目录"弹窗就是一层层调这个接口往下翻文件夹的。
|
||||
package api
|
||||
|
||||
import (
|
||||
@@ -12,33 +13,38 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// FsController 服务器文件系统浏览控制器
|
||||
// FsController 目录浏览的"窗口"。
|
||||
type FsController struct{}
|
||||
|
||||
// NewFsController 创建文件系统浏览控制器
|
||||
// NewFsController 创建目录浏览控制器。
|
||||
func NewFsController() *FsController {
|
||||
return &FsController{}
|
||||
}
|
||||
|
||||
// FsListResult 目录浏览结果
|
||||
// FsListResult 一次目录浏览的结果:
|
||||
// - current:当前在哪个目录(盘符层时为空)
|
||||
// - parent:上一级目录(在根/盘符层时等于 current)
|
||||
// - drives:Windows 的盘符列表(非 Windows 为空)
|
||||
// - dirs:当前目录下的子目录(不含文件)
|
||||
type FsListResult struct {
|
||||
Current string `json:"current"` // 当前目录绝对路径;盘符层为空
|
||||
Parent string `json:"parent"` // 父目录;根/盘符层时等于 Current
|
||||
Drives []string `json:"drives"` // Windows 盘符列表(非 Windows 为空)
|
||||
Dirs []FsDir `json:"dirs"` // 子目录列表(不含文件)
|
||||
Current string `json:"current"`
|
||||
Parent string `json:"parent"`
|
||||
Drives []string `json:"drives"`
|
||||
Dirs []FsDir `json:"dirs"`
|
||||
}
|
||||
|
||||
// FsDir 子目录项
|
||||
// FsDir 子目录项。
|
||||
type FsDir struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
// List GET /api/fs/list?dir= 列出指定目录的子目录(不含文件);
|
||||
// dir 为空时返回 Windows 盘符列表或 Unix 根目录。
|
||||
// List 处理 GET /api/fs/list?dir=xxx:列出指定目录下的子目录。
|
||||
// 没传目录时,Windows 返回盘符列表,其他系统返回根目录。
|
||||
func (c *FsController) List(ctx *gin.Context) {
|
||||
dir := strings.TrimSpace(ctx.Query("dir"))
|
||||
|
||||
// 没指定目录:Windows 先让用户选盘符
|
||||
if dir == "" {
|
||||
if runtime.GOOS == "windows" {
|
||||
Success(ctx, FsListResult{Drives: listLogicalDrives()})
|
||||
@@ -52,7 +58,8 @@ func (c *FsController) List(ctx *gin.Context) {
|
||||
Error(ctx, http.StatusBadRequest, "路径解析失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
// 目录不存在/不可访问时向上回退到最近的已存在父目录,避免阻塞目录选择器
|
||||
// 目录不存在/打不开时,往上找最近的"确实存在"的父目录,
|
||||
// 这样用户输入了不存在的地方也不会卡死,而是回到最近的可用目录。
|
||||
for {
|
||||
st, statErr := os.Stat(abs)
|
||||
if statErr == nil && st.IsDir() {
|
||||
@@ -60,7 +67,7 @@ func (c *FsController) List(ctx *gin.Context) {
|
||||
}
|
||||
parent := filepath.Dir(abs)
|
||||
if parent == abs {
|
||||
// 已到根仍不存在:回退到盘符层
|
||||
// 已经退到根还是不存在:直接返回盘符层
|
||||
if runtime.GOOS == "windows" {
|
||||
Success(ctx, FsListResult{Drives: listLogicalDrives()})
|
||||
return
|
||||
@@ -77,10 +84,12 @@ func (c *FsController) List(ctx *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 上一级目录;在根目录时等于自己
|
||||
parent := filepath.Dir(abs)
|
||||
if parent == abs {
|
||||
parent = abs
|
||||
}
|
||||
// 只收集子目录(跳过文件,这里只需要目录)
|
||||
dirs := make([]FsDir, 0, len(entries))
|
||||
for _, e := range entries {
|
||||
if !e.IsDir() {
|
||||
@@ -88,6 +97,7 @@ func (c *FsController) List(ctx *gin.Context) {
|
||||
}
|
||||
dirs = append(dirs, FsDir{Name: e.Name(), Path: filepath.Join(abs, e.Name())})
|
||||
}
|
||||
// 按名字排序,翻起来不凌乱
|
||||
sort.Slice(dirs, func(i, j int) bool {
|
||||
return strings.ToLower(dirs[i].Name) < strings.ToLower(dirs[j].Name)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user