Как правильно подойти к вопросу: можно ли получить исходное имя файла до переименования из fsnotify?

Ссылка скопирована
Go
0 ответов

Сейчас ситуация такая: пишу на пару с ИИ приложение на Go для windows, в котором есть функция отслеживания изменений в папке: создан, изменен, удален, переименован. Вот по поводу последнего есть проблема. Мне отдают только новое имя файла, старое мне недоступно. Это либо галлюцинации либо действительно только так: ии говорит, что невозможно понять, какое имя было до, потому что:

- renamedFrom (с маленькой буквы) — это неэкспортируемое (приватное) поле внутри структуры fsnotify.Event.
- Правила языка Go запрещают нам напрямую обращаться к приватным полям из другого пакета.
- Наш отладочный вывод log.Printf("%#v", event) мог показать это поле, потому что он использует специальные механизмы "рефлексии", но в обычном коде мы не можем написать event.renamedFrom.

Сейчас ситуация такая: он предлагает решать проблему с помощью эвристики (хранить в буфере rename и ловить следующий create), что я считаю шизой на ранней стадии. При том что в логах мы видим вот такую картину

2025/10/05 22:48:31 [RENAME "C:\CODE\temp\~WRD0000.tmp"] 2025/10/05 22:48:31 [CREATE "C:\CODE\temp\afa.docx" ← "C:\CODE\temp\~WRD0000.tmp"]

Сейчас ситуация такая: как видно, есть указатель при CREATE, если этот CREATE был на основе RENAME. Ну и как он сказал выше, использовать мы эти данные не можем.

По вводным: почему я считаю это галлюцинациями: когда я пытался понять, как внутри fsnotify работает - этот клоун сказал, что внутри ровно эта эвристика и используется. Что является гоневом, потому что в API Windows есть вот такое: FILE_ACTION_RENAMED_NEW_NAME (0x00000005).

По вводным: вот код, который у меня сейчас

package main import ( "log" "path/filepath" "sync" "time" "github.com/fsnotify/fsnotify" ) var ( debounceTimers = make(map[string]*time.Timer) debounceMu sync.Mutex ) var ( // Карта для отслеживания файлов, которые мы меняем сами. internallyModifiedFiles = make(map[string]time.Time) internalModMu sync.Mutex ) func startFileWatcher(folderPath string) { watcher, err := fsnotify.NewWatcher() if err != nil { log.Printf("ОШИБКА: не удалось создать наблюдателя: %v", err) return } defer watcher.Close() go func() { for { select { case event, ok := <-watcher.Events: if !ok { return } // Пропускаем "внутренние" изменения internalModMu.Lock() ignoreUntil, isInternal := internallyModifiedFiles[event.Name] if isInternal && time.Now().Before(ignoreUntil) { internalModMu.Unlock() log.Printf("Watcher: Игнорируем внутреннее изменение для %s", filepath.Base(event.Name)) continue } if isInternal { delete(internallyModifiedFiles, event.Name) } internalModMu.Unlock() // Пропускаем системные/временные файлы baseName := filepath.Base(event.Name) if shouldIgnoreFile(baseName) { continue } // --- ПРОСТАЯ И НАДЕЖНАЯ ЛОГИКА --- // ОБРАБОТКА УДАЛЕНИЯ И ПЕРЕИМЕНОВАНИЯ (как простое удаление) if event.Has(fsnotify.Remove) || event.Has(fsnotify.Rename) { log.Printf("Обнаружено удаление/переименование: %s. Добавляем в очередь.", baseName) relativePath, err := filepath.Rel(settings.LocalPath, event.Name) if err == nil { syncQueue <- SyncEvent{Path: filepath.ToSlash(relativePath), Type: "delete"} } continue } // ОБРАБОТКА СОЗДАНИЯ И ИЗМЕНЕНИЯ (через Debounce) if event.Has(fsnotify.Create) || event.Has(fsnotify.Write) { filePath := event.Name const debounceDuration = 4 * time.Second debounceMu.Lock() timer, exists := debounceTimers[filePath] if exists { timer.Reset(debounceDuration) } else { timer = time.AfterFunc(debounceDuration, func() { log.Printf("Debounce: Таймер для '%s' сработал. Добавляем в очередь.", baseName) syncQueue <- SyncEvent{Path: filePath, Type: "upsert"} debounceMu.Lock() delete(debounceTimers, filePath) debounceMu.Unlock() }) debounceTimers[filePath] = timer } debounceMu.Unlock() } case err, ok := <-watcher.Errors: if !ok { return } log.Println("Ошибка наблюдателя:", err) } } }() err = watcher.Add(folderPath) if err != nil { log.Printf("ОШИБКА: не удалось добавить папку '%s' для отслеживания: %v", folderPath, err) return } log.Printf("Начато отслеживание изменений в папке: %s", folderPath) <-make(chan bool) } func markFileAsInternallyModified(path string) { internalModMu.Lock() defer internalModMu.Unlock() internallyModifiedFiles[path] = time.Now().Add(10 * time.Second) } // shouldIgnoreFile - вспомогательная функция для проверки имени файла. func shouldIgnoreFile(name string) bool { baseName := filepath.Base(name) if baseName == lockFileName || baseName == syncStateFileName || strings.HasPrefix(baseName, "~") { return true } return false }

Нужно решить такую задачу?

Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

Заказать помощь
Другие ответы (0)

Пока нет других ответов. Будьте первым, кто поможет автору.

Ответить на вопрос

комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Вам также может быть интересно