修复: OSS收藏打开+连接指示器根目录+字段映射+侧边栏重构+文件监听+首屏优化
This commit is contained in:
@@ -268,6 +268,11 @@ func (s *FileSystemService) DeletePathWithContext(ctx context.Context, path stri
|
||||
|
||||
// ListDir 列出目录内容
|
||||
func (s *FileSystemService) ListDir(path string) ([]map[string]interface{}, error) {
|
||||
// 根路径:返回 Windows 盘符列表
|
||||
if path == "/" || path == "\\" {
|
||||
return s.listDrives()
|
||||
}
|
||||
|
||||
// 路径验证
|
||||
if err := s.validatePath(path); err != nil {
|
||||
return nil, err
|
||||
@@ -308,6 +313,25 @@ func (s *FileSystemService) ListDir(path string) ([]map[string]interface{}, erro
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// listDrives 列出 Windows 可用盘符
|
||||
func (s *FileSystemService) listDrives() ([]map[string]interface{}, error) {
|
||||
result := make([]map[string]interface{}, 0)
|
||||
for _, drive := range "ABCDEFGHIJKLMNOPQRSTUVWXYZ" {
|
||||
path := string(drive) + ":/"
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
result = append(result, map[string]interface{}{
|
||||
"name": string(drive) + ":",
|
||||
"path": path,
|
||||
"is_dir": true,
|
||||
"size": int64(0),
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CreateDir 创建目录,返回创建的目录信息
|
||||
func (s *FileSystemService) CreateDir(path string) (*FileOperationResult, error) {
|
||||
if err := s.validatePath(path); err != nil {
|
||||
|
||||
138
internal/filewatch/watcher.go
Normal file
138
internal/filewatch/watcher.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package filewatch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
type Watcher struct {
|
||||
watcher *fsnotify.Watcher
|
||||
emitEvent func(name string, data ...any)
|
||||
mu sync.Mutex
|
||||
watched string // 当前监听的文件绝对路径
|
||||
}
|
||||
|
||||
func NewWatcher(emitEvent func(name string, data ...any)) *Watcher {
|
||||
return &Watcher{emitEvent: emitEvent}
|
||||
}
|
||||
|
||||
// WatchFile 开始监听指定文件的变化。切换文件时自动取消旧监听。
|
||||
func (w *Watcher) WatchFile(path string) error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析路径失败: %w", err)
|
||||
}
|
||||
|
||||
// 同一文件不重复监听
|
||||
if absPath == w.watched {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 停止旧监听
|
||||
w.stopLocked()
|
||||
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(absPath); err != nil {
|
||||
return fmt.Errorf("文件不存在: %s", absPath)
|
||||
}
|
||||
|
||||
// 创建 fsnotify watcher
|
||||
fw, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建文件监听器失败: %w", err)
|
||||
}
|
||||
w.watcher = fw
|
||||
|
||||
// fsnotify 在某些系统上不支持直接监听文件,监听其所在目录
|
||||
dir := filepath.Dir(absPath)
|
||||
if err := fw.Add(dir); err != nil {
|
||||
fw.Close()
|
||||
return fmt.Errorf("监听目录失败: %w", err)
|
||||
}
|
||||
|
||||
w.watched = absPath
|
||||
|
||||
// 后台消费事件
|
||||
go w.consumeEvents()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnwatchFile 停止监听
|
||||
func (w *Watcher) UnwatchFile() {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
w.stopLocked()
|
||||
}
|
||||
|
||||
func (w *Watcher) stopLocked() {
|
||||
if w.watcher != nil {
|
||||
w.watcher.Close()
|
||||
w.watcher = nil
|
||||
}
|
||||
w.watched = ""
|
||||
}
|
||||
|
||||
func (w *Watcher) consumeEvents() {
|
||||
debounceDelay := 300 * time.Millisecond
|
||||
var debounceTimer *time.Timer
|
||||
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-w.watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
// 只处理目标文件的 Write/Create/Rename 事件
|
||||
if event.Op&(fsnotify.Write|fsnotify.Create|fsnotify.Rename) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
target := w.watched
|
||||
w.mu.Unlock()
|
||||
|
||||
if target == "" {
|
||||
return // 已停止监听
|
||||
}
|
||||
|
||||
// 路径比较(忽略大小写,Windows)
|
||||
if !strings.EqualFold(event.Name, target) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 防抖
|
||||
if debounceTimer != nil {
|
||||
debounceTimer.Stop()
|
||||
}
|
||||
debounceTimer = time.AfterFunc(debounceDelay, func() {
|
||||
// 文件已不存在则跳过(如被删除)
|
||||
if _, err := os.Stat(target); err != nil {
|
||||
return
|
||||
}
|
||||
if w.emitEvent != nil {
|
||||
w.emitEvent("file-changed", target)
|
||||
}
|
||||
})
|
||||
|
||||
case _, ok := <-w.watcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close 释放资源
|
||||
func (w *Watcher) Close() {
|
||||
w.UnwatchFile()
|
||||
}
|
||||
Reference in New Issue
Block a user