В настоящее время мы используем QFileSystemWatcher, принадлежащий Qt. Из-за ограниченной поддержки в 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();
}
folderChanged
это слот для directoryChanged()
сигнал. И таймер имеет другое соединение для тайм-аута, поэтому, когда время истекло, то обработка должна быть выполнена. Таймер имеет интервал 1 с. Идея заключается в том, что папка не должна обновляться чаще, чем у меня есть интервал, и если она посылает сигналы чаще, чем мне нужно, мне не нужно обрабатывать их немедленно. Скорее я перезапускаю таймер каждый раз, когда выдается сигнал, и у меня есть только одна обработка изменений. Я думаю, что вы можете применить тот же подход.
Другой подход, который может работать для вас, также заключается в проверке даты изменения файла в вашей обработке, и если его текущая дата изменения находится в пределах некоторого эпсилона (небольшой интервал) с вашей датой последней модификации, то у вас есть повторяющийся сигнал и вы не должны реагировать на него. Сохраните эту дату модификации и продолжайте.