我正在使用 golang 观察程序包来观察文件目录。 https://github.com/radovskyb/watcher
唯一的问题是,当文件开始复制/移动时,而不是在文件实际准备好时,会触发 Create 事件。我想知道 Go 中是否有一种巧妙的方法可以在创建事件之后等待,直到文件不再被写入,然后再继续。
我想这种等待很可能必须在 goroutine 中完成,以免阻塞任何其他文件事件。
简单的例子:
package main
import (
"fmt"
"log"
"time"
"github.com/radovskyb/watcher"
)
func main() {
w := watcher.New()
w.FilterOps(watcher.Create, watcher.Write)
go func() {
for {
select {
case event := <-w.Event:
fmt.Println(event) // Print the event's info.
if event.Op == watcher.Create {
// HERE WE DO STUFF
doStuff(event)
}
case err := <-w.Error:
log.Fatalln(err)
case <-w.Closed:
return
}
}
}()
// Watch this folder for changes.
if err := w.Add("./files/"); err != nil {
log.Fatalln(err)
}
// Start the watching process - it'll check for changes every 100ms.
if err := w.Start(time.Millisecond * 100); err != nil {
log.Fatalln(err)
}
}
谢谢!
您正在使用的库无法满足您的要求。 文档说:
watcher 是一个 Go 包,用于监视文件或目录更改(递归或非递归)无需使用文件系统事件,这使其能够一致地跨平台工作。
当我第一次看到这个问题时,我认为通过等待“文件关闭”事件可以轻松解决,该事件会在文件复制完成时发生。 但由于您使用的库拒绝使用特定于平台的 API,因此它无法知道此信息。
使用
inotify
,您可以更高效、更准确地解决您的问题。 有 https://github.com/fsnotify/fsnotify 但它不支持 IN_CLOSE_WRITE
事件(参见 https://github.com/golang/go/issues/15406)。 也许你可以用它作为灵感来编写你自己的 inotify
包装器,它确实支持 IN_CLOSE_WRITE
(可能还有其他一些)。
inotify
的一个显着限制是它不能与 NFS 一起使用,但 watcher
也有限制(并且您不希望在网络共享上使用其轮询机制,至少不会很快)。
另一种方法可以消除 fsnotify 事件,以便在某个时间范围内未发生特定项目的写入事件后调用函数。
示例:
package main
import (
"log"
"sync"
"time"
"github.com/fsnotify/fsnotify"
"github.com/romdo/go-debounce"
)
type debouncerMap struct {
mu sync.RWMutex
m map[string]func()
}
func newDebouncerMap() *debouncerMap {
return &debouncerMap{
m: make(map[string]func()),
}
}
func (dm *debouncerMap) getDebouncer(filename string) func() {
dm.mu.RLock()
debouncer, ok := dm.m[filename]
dm.mu.RUnlock()
if !ok {
dm.mu.Lock()
debouncer, ok = dm.m[filename]
if !ok {
debouncer, _ = debounce.New(100*time.Millisecond, func() {
dm.mu.Lock()
delete(dm.m, filename)
dm.mu.Unlock()
log.Println("modified file:", filename)
})
dm.m[filename] = debouncer
}
dm.mu.Unlock()
}
return debouncer
}
func main() {
// Create new watcher.
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
debouncerMap := newDebouncerMap()
// Start listening for events.
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
//log.Println("event:", event)
if event.Has(fsnotify.Write) {
//log.Println("modified file:", event.Name)
debouncer := debouncerMap.getDebouncer(event.Name)
debouncer()
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("error:", err)
}
}
}()
// Add a path.
err = watcher.Add("C:\\temp\\items")
if err != nil {
log.Fatal(err)
}
// Block main goroutine forever.
<-make(chan struct{})
}