1

目前,我们使用QFileSystemWatcher,属于Qt。由于 Mac OS X 的支持有限,它只能通知我们两个事件:目录更改或文件更改。

但是,后一个事件(文件更改)在其大小稍大并且写入磁盘需要稍长的时间时会多次触发。

我们的解决方法是设置一个计时器以在 1 秒内检查文件。如果在计时器到期之前有更多关于文件的信号出现,我们将重置计时器。

当文件写入磁盘(完成写入)时,有没有办法获得通知?没有必要限制 Qt,任何库都可以。


我们知道 kqueue 监控方法,但是这太低级了,我们不想对每个文件都这样做,因为我们正在监控文件系统的很大一部分。

4

2 回答 2

2

我在一个项目中遇到了同样的问题,最后我决定实现一个原生观察者。这很容易:

在.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;
};

.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();
        }
    }
}
于 2013-03-05T09:40:26.297 回答
1

我有同样的问题,但文件夹。当您将许多文件复制到文件夹时,会发出太多信号,但我只需要一个。所以我有以下解决方案:

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

folderChangeddirectoryChanged()信号槽。并且计时器有另一个超时连接,因此当时间到时,应该进行处理。定时器有 1s 的间隔。它背后的想法是,文件夹的更新频率不应超过我所拥有的间隔,如果它发送信号的频率比我需要的频率高,那么我不需要立即处理它们。相反,每次发出信号时我都会重新启动计时器,并且所有这些我只有一个处理更改。我认为您可以应用相同的方法。

另一种可能对您有用的方法是在您的处理中检查文件修改日期,如果它的当前修改日期在您上次修改日期的某个 epsilon(小间隔)内,那么您有重复信号并且不应该对其做出反应。存储此修改日期并继续。

于 2013-03-04T05:35:52.320 回答