在 Go 中使用目录观察器时,等待文件复制完成

问题描述 投票:0回答:3

我正在使用 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)
        }
}

谢谢!

linux file go
3个回答
2
投票

您正在使用的库无法满足您的要求。 文档说:

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
也有限制(并且您不希望在网络共享上使用其轮询机制,至少不会很快)。


1
投票

fkocik 提交了一个实现 IN_CLOSE_WRITE 的 PR,它在 Linux 上对我来说确实有效。

添加:CloseWrite 事件支持

注意:该代码尚未出现在主分支中,您需要手动检索它。


0
投票

另一种方法可以消除 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{})
}
© www.soinside.com 2019 - 2024. All rights reserved.