简体   繁体   English

适用于Mac OS X的文件系统观察程序

[英]File system watcher for Mac OS X

Currently, we use QFileSystemWatcher, belonging to Qt. 目前,我们使用属于Qt的QFileSystemWatcher。 Due to the limited support in Mac OS X, it can only notify us two events: directory changed, or file changed. 由于Mac OS X中的支持有限,它只能通知我们两个事件:目录已更改或文件已更改。

However, the latter event (file changed) is triggered multiple times when its size is slightly larger and the writing to disk takes slightly longer time. 但是,后一个事件(文件已更改)会在其大小稍大时触发多次,并且写入磁盘需要稍长的时间。

Our workaround is to set up a timer to check the file in 1 second. 我们的解决方法是设置一个计时器来在1秒内检查文件。 If more signals about the file come before timer expires, we reset the timer. 如果在计时器到期之前有关于该文件的更多信号,我们重置计时器。

Is there a way to get the notification when the file is written to the disk (finished writing)? 有没有办法在文件写入磁盘时获得通知(完成写入)? There is no need to limit to Qt, any library could do. 没有必要限制Qt,任何库都可以。


We are aware of the kqueue monitoring method, but that's too low level and we don't want to do that for each file, as we are monitoring a large portion of the file system.. 我们知道kqueue监控方法,但是这个级别太低了,我们不希望为每个文件执行此操作,因为我们正在监视文件系统的大部分内容。

I have the same problem in a project and finally I decide to implement a native watcher. 我在项目中遇到同样的问题,最后我决定实现一个本地观察者。 It's pretty easy: 这很简单:

In the .h: 在.h:

class OSXWatcher : public Watcher
{
public:

    OSXWatcher(const QString& strDirectory);
    virtual ~OSXWatcher();

    virtual bool Start();
    virtual bool Stop();

private:

    /**
     * Callback function of the OS X FSEvent API.
     */
    static void fileSystemEventCallback(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]);

    FSEventStreamRef stream;
};

The .cpp: .cpp:

bool OSXWatcher::Start()
{
    CFStringRef pathToWatchCF = CFStringCreateWithCString(NULL, this->dirToWatch.toUtf8().constData(), kCFStringEncodingUTF8);
    CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&pathToWatchCF, 1, NULL);

    FSEventStreamContext context;
    context.version = 0;
    context.info = this;
    context.retain = NULL;
    context.release = NULL;
    context.copyDescription = NULL;

    stream = FSEventStreamCreate(NULL, &OSXWatcher::fileSystemEventCallback, &context, pathsToWatch, kFSEventStreamEventIdSinceNow, 3.0, kFSEventStreamCreateFlagFileEvents);
    FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    FSEventStreamStart(stream);

    CFRelease(pathToWatchCF);

    // Read the folder content to protect any unprotected or pending file
    ReadFolderContent();
}

bool OSXWatcher::Stop()
{
    FSEventStreamStop(stream);
    FSEventStreamInvalidate(stream);
    FSEventStreamRelease(stream);
}

void OSXWatcher::fileSystemEventCallback(ConstFSEventStreamRef /*streamRef*/, void *clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[])
{
    char **paths = (char **)eventPaths;

    for (size_t i=0; i<numEvents; i++) {
        // When a file is created we receive first a kFSEventStreamEventFlagItemCreated and second a (kFSEventStreamEventFlagItemCreated & kFSEventStreamEventFlagItemModified)
        // when the file is finally copied. Catch this second event.
        if (eventFlags[i] & kFSEventStreamEventFlagItemCreated
                && eventFlags[i] & kFSEventStreamEventFlagItemModified
                && !(eventFlags[i] & kFSEventStreamEventFlagItemIsDir)
                && !(eventFlags[i] & kFSEventStreamEventFlagItemIsSymlink)
                && !(eventFlags[i] & kFSEventStreamEventFlagItemFinderInfoMod)) {

            OSXWatcher *watcher = (OSXWatcher *)clientCallBackInfo;
            if (watcher->FileValidator(paths[i]))
                emit watcher->yourSignalHere();
        }
    }
}

I have the same problem but with folder. 我有同样的问题,但有文件夹。 When you copy many files to the folder too many singals are emitted but I need only one. 当您将许多文件复制到文件夹时,会发出太多的信号,但我只需要一个。 So I have the following solution: 所以我有以下解决方案:

void folderChanged(const QString& folder)
{
    m_pTimerForChanges->start();
}

folderChanged is a slot for directoryChanged() signal. folderChangeddirectoryChanged()信号的插槽。 And timer has another connection for timeout, so when time is out then processing should be done. 并且计时器有另一个超时连接,所以当时间结束时,应该进行处理。 Timer has 1s interval. 定时器有1s间隔。 Idea behind it is that folder should not be updated more frequent than interval I have and if it sends signals more frequently than I need then I don't need to process them immediately. 它背后的想法是文件夹不应该比我的间隔更频繁地更新,如果它比我需要更频繁地发送信号,那么我不需要立即处理它们。 Rather I restart timer every time the signal is emmited and with all of it I have the only one processing of changes. 更确切地说,每次信号发出时我都会重新启动计时器,所有这些都是我唯一一次处理的变化。 I think you can apply the same approach. 我认为你可以采用相同的方法。

Another approach which may work for you also is to check file modification date in your processing and if its current modification date is within some epsilon(small interval) with your last modification date then you have repeating signal and should not react on it. 另一种可能对你有用的方法是检查你的处理中的文件修改日期,如果它的当前修改日期在你上次修改日期的某个epsilon(小间隔)内,那么你有重复的信号,不应对它作出反应。 Store this modification date and proceed. 存储此修改日期并继续。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM