Files
odboy 8979c744a8 refactor: 全面重构代码并加白话注释,移动全局并行,去掉去向/重复记录
- 重构后端与前端,全程大白话注释(不懂程序也能看懂)
- 移动阶段改为全局并行(MoveAll,工人数=CPU核心数)
- 去掉记录每个文件搬去哪了(删除去向回填,解决写库卡顿)
- 去掉重复文件标记与统计:重复判定改为 MD5+路径都相同才算,重复则原地忽略不移动;MD5 相同但路径不同仍照常搬
- 媒体库去掉归类目录列,看板去掉重复/归类分布统计
2025-05-11 21:05:35 +08:00

112 lines
2.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 文件目录浏览接口:给页面上"选择目录"用的,只列目录、不列文件。
// 前端那个"选择目录"弹窗就是一层层调这个接口往下翻文件夹的。
package api
import (
"net/http"
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"github.com/gin-gonic/gin"
)
// FsController 目录浏览的"窗口"。
type FsController struct{}
// NewFsController 创建目录浏览控制器。
func NewFsController() *FsController {
return &FsController{}
}
// FsListResult 一次目录浏览的结果:
// - current:当前在哪个目录(盘符层时为空)
// - parent:上一级目录(在根/盘符层时等于 current)
// - drives:Windows 的盘符列表(非 Windows 为空)
// - dirs:当前目录下的子目录(不含文件)
type FsListResult struct {
Current string `json:"current"`
Parent string `json:"parent"`
Drives []string `json:"drives"`
Dirs []FsDir `json:"dirs"`
}
// FsDir 子目录项。
type FsDir struct {
Name string `json:"name"`
Path string `json:"path"`
}
// 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()})
return
}
dir = string(os.PathSeparator)
}
abs, err := filepath.Abs(dir)
if err != nil {
Error(ctx, http.StatusBadRequest, "路径解析失败: "+err.Error())
return
}
// 目录不存在/打不开时,往上找最近的"确实存在"的父目录,
// 这样用户输入了不存在的地方也不会卡死,而是回到最近的可用目录。
for {
st, statErr := os.Stat(abs)
if statErr == nil && st.IsDir() {
break
}
parent := filepath.Dir(abs)
if parent == abs {
// 已经退到根还是不存在:直接返回盘符层
if runtime.GOOS == "windows" {
Success(ctx, FsListResult{Drives: listLogicalDrives()})
return
}
abs = string(os.PathSeparator)
break
}
abs = parent
}
entries, err := os.ReadDir(abs)
if err != nil {
Error(ctx, http.StatusBadRequest, "读取目录失败: "+err.Error())
return
}
// 上一级目录;在根目录时等于自己
parent := filepath.Dir(abs)
if parent == abs {
parent = abs
}
// 只收集子目录(跳过文件,这里只需要目录)
dirs := make([]FsDir, 0, len(entries))
for _, e := range entries {
if !e.IsDir() {
continue
}
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)
})
Success(ctx, FsListResult{
Current: abs,
Parent: parent,
Drives: nil,
Dirs: dirs,
})
}