1

現在、Qt に属する QFileSystemWatcher を使用しています。Mac OS X ではサポートが限られているため、ディレクトリの変更またはファイルの変更の 2 つのイベントしか通知できません。

ただし、後者のイベント (ファイルが変更された) は、そのサイズがわずかに大きく、ディスクへの書き込みに少し時間がかかる場合に複数回トリガーされます。

私たちの回避策は、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

私は同じ問題を抱えていますが、フォルダに問題があります。多くのファイルをフォルダーにコピーすると、あまりにも多くの信号が出力されますが、必要なのは 1 つだけです。だから私は次の解決策を持っています:

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

folderChanged信号用のスロットですdirectoryChanged()。また、タイマーにはタイムアウト用の別の接続があるため、時間切れになったら処理を行う必要があります。タイマーの間隔は 1 秒です。その背後にあるアイデアは、フォルダーを私が持っている間隔よりも頻繁に更新するべきではなく、必要以上に頻繁にシグナルを送信する場合は、すぐに処理する必要がないということです。むしろ、信号が送信されるたびにタイマーを再起動し、そのすべてで変更の処理が1つだけあります。同じアプローチを適用できると思います。

あなたのために働くかもしれない別のアプローチは、あなたの処理でファイルの変更日をチェックすることです.現在の変更日があなたの最後の変更日といくつかのイプシロン(短い間隔)内にある場合、あなたは繰り返し信号を持っているのでそれに反応するべきではありません. この変更日を保存して続行します。

于 2013-03-04T05:35:52.320 に答える