目前,我们使用属于Qt的QFileSystemWatcher。由于Mac OS X中的支持有限,它只能通知我们两个事件:目录已更改或文件已更改。
但是,后一个事件(文件已更改)在其大小略大时会多次触发,而写入磁盘的时间会稍长。
我们的解决方法是设置一个计时器,以便在1秒内检查文件。如果在计时器到期之前有关于该文件的更多信号,我们将重置计时器。
有没有办法在文件写入磁盘时获得通知(写完)?没有必要限制Qt,任何库都可以。
我们知道kqueue监控方法,但是这个级别太低了,我们不希望为每个文件执行此操作,因为我们正在监视文件系统的大部分内容。
答案 0 :(得分: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();
}
}
}
答案 1 :(得分:1)
我有同样的问题,但有文件夹。当您将许多文件复制到文件夹时,会发出太多的信号,但我只需要一个。所以我有以下解决方案:
void folderChanged(const QString& folder)
{
m_pTimerForChanges->start();
}
folderChanged
是directoryChanged()
信号的插槽。并且计时器有另一个超时连接,所以当时间结束时,应该进行处理。定时器有1s间隔。它背后的想法是文件夹不应该比我的间隔更频繁地更新,如果它比我需要更频繁地发送信号,那么我不需要立即处理它们。更确切地说,每次信号发出时我都会重新启动计时器,所有这些都是我唯一一次处理的变化。我认为你可以采用相同的方法。
另一种可能对你有用的方法是检查你的处理中的文件修改日期,如果它的当前修改日期在你上次修改日期的某个epsilon(小间隔)内,那么你有重复的信号,不应对它做出反应。存储此修改日期并继续。