实现对文件夹的动态检测功能——基于QT

作者:小 琛
欢迎转载,请标明出处

场景

个人遇到的需求:业务中,需要和其它模块对接,完成某类文件的生成、删除…等一系列操作,如果通过和其它模块定接口的方式,所需要的接口量很多并且所需要考虑的细节也很多。同时有一个很重要的点:这些文件是公用的,也就是说需要考虑到同步的情况,即:文件不存在了、文件损坏了要及时同步给ui

动态监控的好处:

  1. 无需关心具体的业务逻辑,只关注某个文件夹的情况,功能独立性高
  2. 配合 线程、信号绑定,不对主流程阻塞

实现流程

思路描述

  1. 单独抽离一个类,继承QThread,重写run()接口
  2. 在run()接口内,即线程启动时,初始化一个QFileSystemWatcher对象,并将需要监控的文件夹目录作为参数绑定
  3. 自定义信号,根据自身业务需求,对QFileSystemWatcher::QFileSystemWatcher以及QFileSystemWatcher::directoryChanged进行信号绑定
  4. 循环监控,直到外部信号终止

QFileSystemWatcher类

QFileSystemWatcher` 是 Qt
提供的一个监视文件和目录变化的类,可以实现在文件或者目录发生变化时发出信号的功能。它可以监视指定文件或目录的创建、删除、重命名、修改等操作,并通过信号通知应用程序

bool QFileSystemWatcher::addPath(const QString &file)

该接口,可以将一个路径作为动态检测的目标,保证传入的路径为可用的字符串,否则会失败
可以用QDir dir(folder_); if (!dir.exists(folder_))

QFileSystemWatcher::fileChanged(const QString &path, QPrivateSignal)

该信号,触发时候为被添加目录下的某个文件,被修改时,在实际使用中,可以拿到path即具体的路径

QFileSystemWatcher::directoryChanged(const QString &path, QPrivateSignal)

该信号,触发时候为被该目录下内容被改变,和fileChanged的区别是:当目录下有新增某个文件、删除某个文件时,触发信号。

QThread的联合使用

QThread 是 Qt 框架提供的一个线程类,用于实现多线程编程。使用 QThread 类可以帮助我们在 Qt 应用程序中实现多线程任务,从而提高应用程序的性能和响应能力。

QThread的使用

  1. 创建一个 QThread 类的子类,用于实现具体的线程任务。这个子类需要重写 run() 函数,用于实现线程执行的代码。

  2. 在主线程中创建这个子类的对象,并调用 start() 函数启动线程。注意,这里只是调用了 start() 函数,实际的线程执行是在新的线程中完成的。

工具:QDir、QStringList、QFileInfoList

在操作文件、文件夹时,QT提供了非常方便的工具类;这里给出几个我用到例子,如果需要其它功能,可以查官网

QDir::exists() 判断路径是否存在;
QDir::mkpath()创建目录
得到某个目录下的所有.zip文件
QDir scriptDir(path);
auto newFileList = scriptDir.entryInfoList(QStringList() << "*.zip", QDir::Files);

例子:对文件夹新增、删除行为进行监控

#pragma once
#include <QObject>
#include <QThread>
#include <QMetaType>

class FolderMonitoring : public QThread {
    Q_OBJECT
public:
    enum ChangeType { Add, Delete};
    FolderMonitoring (QObject* parent, QString& folder);


    virtual ~FolderMonitoring () = default;

protected:
    virtual void run() override;

signals:
    void ChangeSignals(QFileInfoList list, ChangeType type);
    void FileContentChange(const QString& file);

private slots:
    void onFolderModified(const QString& path);
    void onFileModified(const QString& path);

private:
    QString folder_;
    QFileInfoList fileInfoList_;
    //QStringList fileList_;
};

Q_DECLARE_METATYPE(FolderMonitoring::ChangeType)
#include <QFile>
#include <QDir>
#include "FolderMonitoring.h"
#include "../../shell/utils/FileSystemWatcher.h"

FolderMonitoring::FolderMonitoring(QObject* parent, QString& folder) : QThread(parent), folder_(folder) {
    // 获取文件夹中的所有文件
    qRegisterMetaType<ScriptChangeType>("ScriptChangeType");
    QDir scriptDir(folder);
    fileInfoList_ = scriptDir.entryInfoList(QStringList() << "*.mmor", QDir::Files);
}

void FolderMonitoring::run() {
    if (folder_.isEmpty()) return;
    FileSystemWatcher* filesWatcher = new FileSystemWatcher();
    connect(filesWatcher , &QFileSystemWatcher::directoryChanged, this, &FolderMonitoring::onFolderModified);
    connect(filesWatcher , &QFileSystemWatcher::fileChanged,this, &FolderMonitoring::onFileModified);
    // 文件夹不存在则创建
    QDir dir(folder_);
    if (!dir.exists(folder_)) {
        dir.mkpath(".");
        // todo: 处理失败
    }
    filesWatcher->addPath(folder_);

    QThread::exec(); // 循环监控,直到外部信号终止
}

void FolderMonitoring::onFileModified(const QString& path) { emit FileContentChange(path); }

void FolderMonitoring::onFolderModified(const QString& path) {
    QDir scriptDir(path);
    auto newFileList = scriptDir.entryInfoList(QStringList() << "*.mmor", QDir::Files);
    
    if (path == folder_) {
        // 获取目录下的文件列表
        QFileInfoList fileList = QDir(folder_).entryInfoList(QStringList() << "*.mmor", QDir::Files);

        // 查找新增的.mmor文件
        QFileInfoList addedFiles = fileList;
        for (const auto& file : fileInfoList_) {
            addedFiles.removeAll(file);
        }

        // 查找删除的.mmor文件
        QFileInfoList removedFiles = fileInfoList_;
        for (const auto& file : fileList) {
            removedFiles.removeAll(file);
        }

        // 发射信号
        if (!addedFiles.isEmpty()) {
            emit scriptChangeSignals(addedFiles, ScriptAdd);
        }
        if (!removedFiles.isEmpty()) {
            emit scriptChangeSignals(removedFiles, ScriptDelete);
        }

        // 更新文件列表
        fileInfoList_ = fileList;
    }
}

更优质的实现:poco+Qt

Poco 是一个C++类库集合,提供了许多可重用的组件和工具,用于开发跨平台的应用程序和系统。Poco的全称是"POrtable COmponents",这个名字反映了它的设计目标:提供一组轻量级、可移植的C++类库,以便开发者能够更快、更简单地构建高质量的软件。
Poco库覆盖了许多领域,包括网络和互联网编程、应用程序框架、文件系统和文件处理、数据库访问、加密和安全、多线程和并发编程、XML处理等。它提供了许多现代软件开发所需的功能,让开发人员能够更专注于业务逻辑的实现,而不必关心底层的系统细节。

Poco::DirectoryWatcher为我们直接提供了监控、信号发射等功能,我们只需结合自己的需求,上层封装即可,下面给出一个例子

#pragma once
#include <QObject>
#include <QMetaType>
#include <Poco/Delegate.h>
#include <Poco/DirectoryWatcher.h>

class FolderMonitor: public QObject {
    Q_OBJECT
public:
    FolderMonitor(QString& folder);
    ~FolderMonitor() = default;

    void onFileAdded(const Poco::DirectoryWatcher::DirectoryEvent& evt);
    void onFileRemoved(const Poco::DirectoryWatcher::DirectoryEvent& evt);
    void onFileModified(const Poco::DirectoryWatcher::DirectoryEvent& evt);

signals:
    void fileModified(const QString& filepath);
    void fileRemoved(const QString& filepath);
    void fileAdded(const QString& filepath);

private:
    Poco::DirectoryWatcher watcher_;
};

```cpp
#include <QFile>
#include <QDir>
#include <QFileSystemWatcher>
#include "FolderMonitor.h"

FolderMonitor::FolderMonitor(QString& folder)
 : watcher_(folder.toStdString()) {
    watcher_.itemAdded += Poco::delegate(this, &FolderMonitor::onFileAdded);
    watcher_.itemRemoved += Poco::delegate(this, &FolderMonitor::onFileRemoved);
    watcher_.itemModified += Poco::delegate(this, &FolderMonitor::onFileModified);
}

void FolderMonitor::onFileAdded(const Poco::DirectoryWatcher::DirectoryEvent& evt) {
    auto path = evt.item.path();
    emit fileAdded(QString::fromStdString(path));
}

void FolderMonitor::onFileRemoved(const Poco::DirectoryWatcher::DirectoryEvent& evt) {
    auto path = evt.item.path();
    emit fileRemoved(QString::fromStdString(path));
}

void FolderMonitor::onFileModified(const Poco::DirectoryWatcher::DirectoryEvent& evt) {
    auto path = evt.item.path();
    emit fileModified(QString::fromStdString(path));
}

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值