
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簡單的封裝實現對單個目錄內文件變化的監視,外部通過信號槽方式進行后續處理(比如文件的上傳、同步等)。其實本例子程序還可以做不少拓展,比如多目錄同時監控、目錄遞歸監控等,實現可能會稍稍復雜一點,但是再復雜的功能也是從簡單的開始,有個好的開端總是很重要的。
本例子程序如果對多文件同時刪除,可能獲取不准確,盡量進行單文件操作。
