簡體   English   中英

適用於Mac OS X的文件系統觀察程序

[英]File system watcher for Mac OS X

目前,我們使用屬於Qt的QFileSystemWatcher。 由於Mac OS X中的支持有限,它只能通知我們兩個事件:目錄已更改或文件已更改。

但是,后一個事件(文件已更改)會在其大小稍大時觸發多次,並且寫入磁盤需要稍長的時間。

我們的解決方法是設置一個計時器來在1秒內檢查文件。 如果在計時器到期之前有關於該文件的更多信號,我們重置計時器。

有沒有辦法在文件寫入磁盤時獲得通知(完成寫入)? 沒有必要限制Qt,任何庫都可以。


我們知道kqueue監控方法,但是這個級別太低了,我們不希望為每個文件執行此操作,因為我們正在監視文件系統的大部分內容。

我在項目中遇到同樣的問題,最后我決定實現一個本地觀察者。 這很簡單:

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

我有同樣的問題,但有文件夾。 當您將許多文件復制到文件夾時,會發出太多的信號,但我只需要一個。 所以我有以下解決方案:

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

folderChangeddirectoryChanged()信號的插槽。 並且計時器有另一個超時連接,所以當時間結束時,應該進行處理。 定時器有1s間隔。 它背后的想法是文件夾不應該比我的間隔更頻繁地更新,如果它比我需要更頻繁地發送信號,那么我不需要立即處理它們。 更確切地說,每次信號發出時我都會重新啟動計時器,所有這些都是我唯一一次處理的變化。 我認為你可以采用相同的方法。

另一種可能對你有用的方法是檢查你的處理中的文件修改日期,如果它的當前修改日期在你上次修改日期的某個epsilon(小間隔)內,那么你有重復的信號,不應對它作出反應。 存儲此修改日期並繼續。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM