1 需求描述
- 實現一個類能夠監控單個目錄內文件的變化;
- 能夠識別文件的創建、改變、刪除三種變化。
2 設計思路
Qt自帶的QFileSystemWatcher提供了一些接口,通過addPath添加一個路徑即可對該目錄進行監視,當目錄發生改變時(比如添加和刪除文件)會發出directoryChanged信號,還有個fileChanged信號主要針對單個文件,這里不涉及。
對於創建了什么文件,刪除了什么文件,以及文件名從什么改變成了什么,這些都是無法直接獲取到的,如果監視目錄后有以下三個信號豈不美哉。
//文件創建信號
void fileCreated(const QString &fileName);
//文件名改變信號
void fileNameChanged(const QString &previousFileName, const QString ¤tFileName);
//文件刪除信號
void fileRemoved(const QString &fileName);
下面開始造,實現很簡單,維護一個文件列表(目錄改變前),被監視目錄改變后在槽函數中和當前文件列表(目錄改變后)進行比對,從而判斷文件的變化狀態,然后發射對應的信號。
3 代碼實現
3.1 添加監視目錄
繼承QFileSystemWatcher,然后添加一個監視目錄,相關代碼如下:
CustomFileWatcher::CustomFileWatcher(QObject *parent) :
QFileSystemWatcher(parent)
{
connect(this, &CustomFileWatcher::directoryChanged, this, &CustomFileWatcher::onDirectoryChanged);
}
CustomFileWatcher::CustomFileWatcher(const QString &path, QObject *parent) :
QFileSystemWatcher(QStringList(path), parent)
{
QFileSystemWatcher::addPath(path);
QDir dir(path);
dir.setFilter(QDir::NoDotAndDotDot | QDir::AllEntries);
m_fileEntryList = dir.entryList();
connect(this, &CustomFileWatcher::directoryChanged, this, &CustomFileWatcher::onDirectoryChanged);
}
void CustomFileWatcher::addPath(const QString &path)
{
if (!directories().isEmpty()) {
removePath(directories().first());
}
QFileSystemWatcher::addPath(path);
QDir dir(path);
dir.setFilter(QDir::NoDotAndDotDot | QDir::AllEntries);
m_fileEntryList = dir.entryList();
}
3.2 判斷文件狀態
進入目錄改變槽函數,內部通過文件列表(變化前和變化后)的比對,從而判斷目錄內文件的變化狀態,發射對應信號,代碼如下:
void CustomFileWatcher::onDirectoryChanged(const QString &path)
{
QDir dir(path);
dir.setFilter(QDir::NoDotAndDotDot | QDir::AllEntries);
QStringList fileEntryList = dir.entryList();
int previousFileCount = m_fileEntryList.count();
int currentFileCount = fileEntryList.count();
if (previousFileCount == currentFileCount) {
for (int i = 0; i < previousFileCount; i++) {
QString previousFileName = m_fileEntryList.at(i);
if (!fileEntryList.contains(previousFileName)) {
for (int j = 0; j < currentFileCount; j++) {
QString currentFileName = fileEntryList.at(j);
if (!m_fileEntryList.contains(currentFileName)) {
emit fileNameChanged(previousFileName, currentFileName);
m_fileEntryList = fileEntryList;
return;
}
}
}
}
} else if (previousFileCount > currentFileCount) {
for (int i = 0; i < currentFileCount; i++) {
m_fileEntryList.removeOne(fileEntryList.at(i));
}
emit fileRemoved(m_fileEntryList.first());
m_fileEntryList = fileEntryList;
} else {
QStringList tempList = fileEntryList;
for (int i = 0; i < previousFileCount; i++) {
tempList.removeOne(m_fileEntryList.at(i));
}
emit fileCreated(tempList.first());
m_fileEntryList = fileEntryList;
}
}
到此,單目錄內文件監控功能就算完成了。
使用的時候只需要添加監視目錄再連接相關信號槽即可,目錄內文件的變化狀態就能獲取到了,是不是很簡單呢?
connect(&m_fileWatcher, &CustomFileWatcher::fileNameChanged, this, &MainWindow::onFileNameChanged);
connect(&m_fileWatcher, &CustomFileWatcher::fileCreated, this, &MainWindow::onFileCreated);
connect(&m_fileWatcher, &CustomFileWatcher::fileRemoved, this, &MainWindow::onFileRemoved);
4 總結
根據需求,通過對QFileSystemWatcher簡單的封裝實現對單個目錄內文件變化的監視,外部通過信號槽方式進行后續處理(比如文件的上傳、同步等)。其實本例子程序還可以做不少拓展,比如多目錄同時監控、目錄遞歸監控等,實現可能會稍稍復雜一點,但是再復雜的功能也是從簡單的開始,有個好的開端總是很重要的。
本例子程序如果對多文件同時刪除,可能獲取不准確,盡量進行單文件操作。