前言

该项目是博主上大学时抽到的课程设计的一个题目,涉及到QT的UI设计和控件操作、MySql数据库和文件操作,综合性较强,但由于学校规定制作的时间太短,没办法将该项目精益求精,该文章只是作简单记录,技术过于落后,不出售也不分享源码!

一、设计要求

设计一个简单的二级文件系统,主要实现以下功能:

实现下列命令中至少4条:

Login用户登录

Dir列文件目录

Create创建文件

Delete删除文件

Open打开文件

Close关闭文件

Read读文件

Write写文件

列目录时要列出文件名和文件长度

源文件可以进行读写保护

开发环境:QT creater6.5

二、需求分析

1.功能需求:

能实现文件和目录的创建、删除和重命名。

文件和目录的读取和写入。

支持文件和目录的权限管理,例如读、写权限。

支持文件和目录的属性查询,例如大小、创建时间和修改时间等。

支持文件和目录的搜索和遍历。

2.性能需求

系统应该能够快速响应用户的请求,不会出现明显的延迟。

系统应该能够高效地处理大量的文件和目录,不会出现性能瓶颈。

系统应该能够保证数据的完整性和安全性,不会出现数据丢失或损坏的情况。

3.可靠性需求

系统应该能够在出现故障时进行自我修复,保证系统的可用性。

系统应该能够保证数据的一致性和可靠性,不会出现数据丢失或损坏的情况。

系统应该能够保证用户的数据隐私和安全,不会出现数据泄露的情况。

4.易用性需求

系统应该能够提供友好的用户界面,方便用户进行操作。

系统应该能够提供详细的帮助文档和使用说明,方便用户了解系统的功能和使用方法。

系统应该能够提供错误提示和异常处理机制,方便用户进行故障排除和问题解决。

5.可维护性需求

系统应该能够提供清晰的代码结构和注释,能够方便开发人员进行维护和修改。

系统应该能够提供完善的文档和测试用例,能够方便开发人员进行测试和验证。

三、概要设计

功能模块初步认定设为3个,分别是用户登录模块、注册模块、用户操作模块,对应3个UI界面。其中在用户操作模块中又通过stacked Widget控件设计了8个子模块,分别是创建文件模块、写入文件模块、读取文件模块、删除文件模块、文件信息模块、更改属性模块、文件目录模块。

模块之间的关系图如图3-1所示。

图3-1功能模块关系图

 四、详细设计

1.登录模块设计

登录模块的UI界面如图4-1所示。

图4-1登录界面UI

登录模块的代码实现中,首先是进行数据库连接,然后实例化用户操作模块对象,接着是登录操作的具体逻辑,然后是实例化注册模块对象,接着是进入注册操作的具体逻辑,最后就是监听,注册模块和用户操作模块的返回信号逻辑。 

在登录模块中定义了全局变量login_user_name用来向用户操作模块传用户名。

连接数据库采用ODBC方法

QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
    db.setHostName("127.0.0.1");//自己主机IP
    db.setPort(3306);//数据库接口
    db.setDatabaseName("op");//ODBC中的连接名
    db.setUserName("root");//数据库中的用户名
    db.setPassword("123456");//数据库中的用户密码
    bool ok = db.open();
    if(ok){
        qDebug()<<"数据库已连接";
    }else{
        qDebug()<<"数据库连接失败";
    }

登录模块逻辑:

按下登录按钮后根据输入框中用户名在数据库中查找该用户名若无用户名则弹窗警告,若有该用户名则比对数据库中的用户名密码和当前输入框中的密码,若密码不同则弹窗警告,相同则进入用户操作界面。

按下注册按钮则是进入注册界面。

另外,登录模块会监听用户操作界面和注册界面的退出信号logout和back_to_login
它们分别定义在用户操作模块的头文件和注册模块的头文件中,如图4-2和图4-3所示。

图4-2

图4-3

登录模块具体逻辑代码:

    //登录
    user_interface *user_int = new user_interface();
    connect(ui->btn_login,&QPushButton::clicked,[=]{
        login_user_name = ui->user_name->text();
        QString user_password = ui->user_password->text();
        user_password.toInt();
        QSqlQuery result(db);
        result.exec("select * from user");
        bool flag = false;
        while(result.next()){
            if(login_user_name == result.value("user_name").toString()){
                if(user_password == result.value("user_password")){
                    flag = true;
                }
                break;
            }

        }
        if(!flag){
            qDebug()<<"用户名或密码错误";
            QMessageBox::warning(this,"警告","用户名或密码错误");
            ui->user_name->clear();
            ui->user_password->clear();
        }else{
            qDebug()<<"登录成功";
            QMessageBox::information(this,"提示信息","用户登录成功");
            
            qDebug()<<login_user_name;
            this->hide();
            
            user_int->show();
        }
    });
    //监听用户界面的注销用户信号 接收到了就隐藏用户界面 显示登录界面
    connect(user_int,&user_interface::logout,[=]{
        user_int->hide();
        this->show();
        ui->user_name->clear();
        ui->user_password->clear();
    });


    //注册
    regiset_ui *r = new regiset_ui;
    //点击注册按钮->登录界面隐藏->注册界面出现
    connect(ui->btn_register,&QPushButton::clicked,[=]{
        this->hide();
        r->show();
    });
    //监听注册界面的返回信号 接收到就隐藏注册界面 显示登录界面
    connect(r,&regiset_ui::back_to_login,[=]{
        r->hide();
        this->show();
    });

2.注册模块设计 

注册模块的UI界面如图4-4所示。

图4-4注册模块UI

注册模块的代码实现中,首先是创建数据库连接,然后是注册逻辑实现,注册逻辑中包含了检测二次输入密码相同与数据库和用户名比对。点击确认登录按钮后,系统先检查两次输入密码是否相同,相同则查询数据库中的用户表,若新用户名没有被创建则创建成功,加入新用户信息,且在指定区域生成用户文件夹。

返回按钮,点击返回按钮发送back_to_login()的信号量给登录模块登录模块接收后,在登录模块中实现将注册页面隐藏登录界面显示。 

注册模块具体逻辑代码:

    //连接数据库
    QSqlDatabase regist_db = QSqlDatabase::addDatabase("QODBC");
    regist_db.setHostName("127.0.0.1");
    regist_db.setPort(3306);
    regist_db.setDatabaseName("op");
    regist_db.setUserName("root");
    regist_db.setPassword("123456");
    bool ok = regist_db.open();
    if(ok){
        qDebug()<<"数据库已连接";
    }else{
        qDebug()<<"数据库连接失败";
    }

    connect(ui->btn_regist,&QPushButton::clicked,[=]{
        QString user_name = ui->regist_user_name->text();
        QString user_password = ui->regist_user_password->text();
        QString user_password_again = ui->regist_user_password_again->text();
        user_password.toInt();
        QSqlQuery result(regist_db);
        result.exec("select * from user");
        bool flag1 = true;
        bool flag2 = true;
        if(user_password != user_password_again){
            qDebug()<<"请输入相同密码";
            QMessageBox::warning(this,"警告","请输入相同密码");
            ui->regist_user_name->clear();
            ui->regist_user_password->clear();
            ui->regist_user_password_again->clear();
            flag1 = false;//flag1 false代表密码重复
            flag2 = false;
        }

        while(result.next()){
            if(!flag1){break;}
            if(user_name == result.value("user_name"))
            {
                qDebug()<<"该用户名已存在";
                QMessageBox::warning(this,"警告","该用户名已存在");
                ui->regist_user_name->clear();
                ui->regist_user_password->clear();
                ui->regist_user_password_again->clear();
                flag2 = false;
                break;
            }
        }

        if(flag2){
            result.prepare("insert into user values (:name,:password)");
            result.bindValue(":name",user_name);
            result.bindValue(":password",user_password);
            if(result.exec()){
                qDebug()<<"创建用户成功";
                QDir *dir = new QDir;
                QString path = "G:\\QT\\QT_CODE\\op\\file_source\\"+user_name;
                if(dir->exists(path)){
                    QMessageBox::warning(this,"警告","该用户已存在");
                }else{
                    dir->mkdir(path);
                    QMessageBox::information(this,"提示信息","创建用户成功");
                    emit this->back_to_login();
                }
            }
        }

    });


    //返回按钮逻辑
    connect(ui->btn_back_to_login,&QPushButton::clicked,[=]{
        emit this->back_to_login();
    });

 3.用户操作模块设计

用户操作界面UI(部分)如图4-5所示。

图4-5

用户操作模块中,由八个子模块构成,分别是:创建文件模块、写入文件模块、读取文件模块、删除文件模块、文件信息模块、更改属性模块、文件目录模块、退出登录模块。子模块之间关系如图4-6所示。

图4-6子模块关系图

 由于用户操作模块使用了stacked Widget 所以需要将左侧按钮与指定的stackedWidget中的内容进行连接。代码如下所示:

    //页面切换逻辑
    connect(ui->btn_choose_create_file,&QPushButton::clicked,[=](){
        ui->stackedWidget->setCurrentIndex(0);
    });
    connect(ui->btn_choose_write_file,&QPushButton::clicked,[=](){
        ui->stackedWidget->setCurrentIndex(1);
    });
    connect(ui->btn_choose_read_file,&QPushButton::clicked,[=](){
        ui->stackedWidget->setCurrentIndex(2);
    });
    connect(ui->btn_choose_delete_file,&QPushButton::clicked,[=](){
        ui->stackedWidget->setCurrentIndex(3);
    });
    connect(ui->btn_choose_file_infomation,&QPushButton::clicked,[=](){
        ui->stackedWidget->setCurrentIndex(4);
    });
    connect(ui->btn_choose_attribute,&QPushButton::clicked,[=]{
        ui->stackedWidget->setCurrentIndex(5);
    });
    connect(ui->btn_choose_list,&QPushButton::clicked,[=](){
        ui->stackedWidget->setCurrentIndex(6);
    });

1)创建文件模块设计

用户点击用户操作页面的创建文件按钮进入创建文件页面,在指定区域中输入文件名和文件内容内容,再点击创建按钮,系统会查询用户目录下的文件的文件名,若用户目录下的文件中没有该文件名的文件则创建成功,否则创建失败,创建成功后,文件表中会更新一条用户名和文件名的信息,同时,在指定用户目录下会新增一个文件名.txt的文件。代码如下所示:

    //创建文件逻辑
    connect(ui->btn_create_file,&QPushButton::clicked,[=]{
        QString file_name = ui->create_file_name->text();
        QString path;
        qDebug()<<login_user_name+"欢迎登录!";
        path ="G:\\QT\\QT_CODE\\op\\file_source\\"+login_user_name+"\\"+file_name+".txt";
        qDebug()<<path;
        QFile file(path);
        if(file.exists()){
            QMessageBox::warning(this,"警告","文件已存在");
        }else
        {
            file.open(QIODevice::Append);
            QTextDocument *write_content = ui->create_file_text_edit->document();
            int cols = write_content->blockCount();
            for(int i = 0 ; i < cols ; i++){
                QTextBlock textBlock = write_content->findBlockByNumber(i);
                QString str = textBlock.text();
                str ="\n" + str + "\n";
                file.write(str.toUtf8());
            }
            QMessageBox::information(this,"提示信息","文件已创建成功");
            file.close();
            QSqlQuery result(user_db);
            result.prepare("insert into user_file_table values (:name,:file_name,:read,:write)");
            result.bindValue(":name",login_user_name);
            result.bindValue(":file_name",file_name);
            result.bindValue(":read",1);
            result.bindValue(":write",1);
            result.exec();
        }
    });

2) 写入文件模块设计

用户点击用户操作界面的写入文件按钮,进入写入文件页面,在指定区域内填入文件名和文件内容后,再点击写入按钮,系统会检查该文件是否存在,若不存在则写入失败,若存在则检查该文件是否可写,若可写则写入成功,反之写入失败。代码如下所示:

    //写入文件操作
    connect(ui->btn_write_file,&QPushButton::clicked,[=]{
        QString file_name = ui->write_file_name->text();
        QString path;
        path ="G:\\QT\\QT_CODE\\op\\file_source\\"+login_user_name+"\\"+file_name+".txt";
        QFile file(path);
        if(file.exists())
        {
            bool flag = true;
            //检查是否可写
            QSqlQuery result2(user_db);
            result2.prepare("select * from user_file_table");
            result2.exec();
            while(result2.next()){
                if(login_user_name == result2.value("user_name")&&file_name ==result2.value("file_name")){
                    if(result2.value("can_write")==1){
                        flag = false;
                        break;}}}
            if(flag){
                QMessageBox::warning(this,"警告","该文件不可写");
            }else
            {
                file.open(QIODevice::Append);
                QTextDocument *write_content = ui->write_file_text_edit->document();
                int cols = write_content->blockCount();
                for(int i = 0 ; i < cols ; i++){
                    QTextBlock textBlock = write_content->findBlockByNumber(i);
                    QString str = textBlock.text();
                    str ="\n" + str + "\n";
                    file.write(str.toUtf8());
                }
                qDebug()<<"写入完毕";
                QMessageBox::information(this,"提示信息","写入完毕");
                ui->write_file_name->text().clear();
                ui->write_file_text_edit->clear();
                file.close();
            }
        }else
        {QMessageBox::warning(this,"警告","该文件不存在");}
    });

3)读取文件模块设计

用户点击读取文件按钮进入读取文件界面,在指定区域内填入文件名,再点击读取按钮,系统会检查该文件是否存在,若存在,则将文件内容显示在规定区域内,若该文件不存在则读取失败。代码如下所示:

    //文件读取操作
    connect(ui->btn_read_file,&QPushButton::clicked,[=]{
        //获取路径
        QString file_name = ui->read_file_name->text();
        QString path;
        path ="G:\\QT\\QT_CODE\\op\\file_source\\"+login_user_name+"\\"+file_name+".txt";
        qDebug()<<path;
        //获取到路径之后读取信息到textEdit里面
        QFile file(path);
        if(file.exists())
        {   bool flag = true;
            //检查是否可读
            QSqlQuery result2(user_db);
            result2.prepare("select * from user_file_table");
            result2.exec();
            while(result2.next()){
                if(login_user_name == result2.value("user_name")&&file_name ==result2.value("file_name")){
                    if(result2.value("can_read")==1){
                        flag = false;
                        break;}}}
            if(flag){
                QMessageBox::warning(this,"警告","该文件不可读");
            }else
            {   file.open(QIODevice::ReadOnly);
                QByteArray content = file.readAll();
                ui->read_file_text_edit->setText(content);}
        }else
        {QMessageBox::warning(this,"警告","该文件不存在");}
    });

4)删除文件模块设计

用户点击删除文件按钮进入删除文件界面,在指定区域内填入想要删除的文件名后点击删除按钮,系统先检查该文件是否存在,若不存在则删除失败,若存在则删除该用户目录下的文件以及数据库的文件表中的信息。

//删除文件操作
    connect(ui->btn_delete_file,&QPushButton::clicked,[=]{
        //获取文件路径
        QString file_name = ui->delet_file_path->text();
        QString path ="G:\\QT\\QT_CODE\\op\\file_source\\"+login_user_name+"\\"+file_name+".txt";

        QFile file(path);
        if(file.exists())
        {   file.remove();
            QSqlQuery result2(user_db);
            QString str = "delete from user_file_table where user_name='"+login_user_name+"' and file_name = '"+file_name+"';";
            qDebug()<<str;
            if(result2.exec(str)){
                qDebug()<<"执行成功";
            };
            QMessageBox::information(this,"提示信息","删除文件成功");
        }else
        {QMessageBox::warning(this,"警告","该文件不存在");}
    });

5)文件信息模块设计

用户点击文件信息按钮进入文件信息界面,在指定区域内输入文件名,再点击显示信息按钮,系统会检查该用户目录下是否存在该文件,若存在则在指定区域内输出文件大小、文件的创建时间、文件的最后修改时间、文件的最后访问时间,然后访问数据库中的文件表,查询该文件名对应的表单项中的可读可写属性,然后将可读可写属性输出到文件信息模块的指定区域中。实现该模块的逻辑是首先需要获取输入文件名的文件路径,通过文件路径检查该文件是否存在,若不存在则查询文件信息失败,若存在则通过创建文件对象,通过birthTime()、lastModified()、lastRead()方法获取文件的创建时间、文件的最后修改时间、文件的最后访问时间,再根据数据库内容,通过setCheckState()方法将数据库中的文件属性显示在文件信息界面的单选框中。代码如下所示:

    //获取文件信息操作
    connect(ui->btn_file_information,&QPushButton::clicked,[=]{
        QString file_name = ui->info_file_name->text();
        QString path = "G:\\QT\\QT_CODE\\op\\file_source\\"+login_user_name+"\\"+file_name+".txt";
        QFile file(path);
        if(file.exists())
        {
            QFileInfo file_info(path);
            //文件大小
            qint64 file_size = file_info.size();
            ui->file_size->setText(QString::number(file_size));
            //创建时间
            QDateTime create_time = file_info.birthTime();
            ui->file_create_time->setText(create_time.toString("yyyy/MM/dd hh:mm:ss"));
            //最后修改时间
            QDateTime modify_time = file_info.lastModified();
            ui->file_last_modify_time->setText(modify_time.toString("yyyy/MM/dd hh:mm:ss"));
            //最后访问时间
            QDateTime ask_time = file_info.lastRead();
            ui->file_last_ask_time->setText(ask_time.toString("yyyy/MM/dd hh:mm:ss"));
            //获取文件属性
            QSqlQuery result2(user_db);
            result2.prepare("select * from user_file_table");
            result2.exec();
            while(result2.next()){
                if(login_user_name == result2.value("user_name")&&file_name ==result2.value("file_name")){
                    if(result2.value("can_read")==1){
                        ui->check_read->setCheckState(Qt::Checked);
                    }else
                    {
                        ui->check_read->setCheckState(Qt::Unchecked);
                    }
                    if(result2.value("can_write")==1){
                        ui->check_write->setCheckState(Qt::Checked);
                    }else
                    {
                        ui->check_write->setCheckState(Qt::Unchecked);
                    }
                }}

        }else
        {
            QMessageBox::warning(this,"警告","该文件不存在");
        }
    });

6)更改属性模块设计

用户点击更改属性按钮进入更改属性界面,在指定区域内填入文件名,再在可读可写属性的单选框内打勾与否设置文件可读可写属性,然后点击更改属性按钮,系统会查询用户目录下该文件是否存在,若该文件存在则修改属性,反之修改失败。代码如下:

    //更改文件属性
    connect(ui->btn_modify_attribute,&QPushButton::clicked,[=]{
        //获取数据库对象
        QString file_name = ui->modify_attribute_text_edit->text();
        QSqlQuery result2(user_db);
        QString str;
        //获取标志
        if(ui->modify_read_box->checkState()==Qt::Checked){
            str = "update user_file_table set can_read = 1 where user_name = '"+login_user_name+"'and file_name = '"+file_name+"'";
            qDebug()<<str;
            result2.exec(str);
        }else
        {   str = "update user_file_table set can_read = 0 where user_name = '"+login_user_name+"' and file_name = '"+file_name+"'";
            qDebug()<<str;
            result2.exec(str);}
        if(ui->modify_write_box->checkState()==Qt::Checked){
            str = "update user_file_table set can_write = 1 where user_name = '"+login_user_name+"' and file_name = '"+file_name+"'";
            qDebug()<<str;
            result2.exec(str);
        }else{
            str = "update user_file_table set can_write = 0 where user_name = '"+login_user_name+"' and file_name = '"+file_name+"'";
            qDebug()<<str;
            result2.exec(str);}
    });

7)文件目录模块设计

用户点击文件目录按钮进入文件目录界面,文件目录界面会立刻在指定区域内显示该用户所拥有的每个文件的文件名以及每个文件所对应的大小。代码如下所示:

    //列出用户文件
    connect(ui->btn_choose_list,&QPushButton::clicked,[=](){
        ui->textBrowser->clear();
        //获取数据库对象
        QSqlQuery result2(user_db);
        QString msg;
        QString command = "select * from user_file_table where user_name = '"+login_user_name+"'";
        QString file_name;
        QString path;

        result2.exec(command);
        while(result2.next()){
            file_name = result2.value("file_name").toString();
            path = "G:\\QT\\QT_CODE\\op\\file_source\\"+login_user_name+"\\"+file_name+".txt";
            QFileInfo file_info(path);
            qint64 file_size = file_info.size();
            msg = file_name+"------文件大小:"+QString::number(file_size)+"字节";
            ui->textBrowser->append(msg);}
    });

8)退出登录模块

用户点击退出登录按钮,向登录模块发出logout()信号量,退出用户操作界面,进入登录界面。代码如下所示:

    //退出登录按钮逻辑
    connect(ui->btn_logout,&QPushButton::clicked,[=]{
        //按下返回按钮 发送退出登录界面信号量
        emit this->logout();
    });

五、运行示例

注册一个新用户,注册时,如果两次密码输入不一样则会出现弹窗警告,如图 5‑1所示。如果用户名已被注册过了也会出现弹窗警告,如图 5‑2所示。

5‑1两次密码输入不同情况

5‑2注册用户已存在情况

保证注册的新用户名没有与之前已注册过的用户名同名而且两次输入的密码相同的情况下,才能够注册成功,如图 5‑3所示。

5‑3注册成功

注册成功的同时,数据库中的用户表会进行更新,如图 5‑4所示。

5‑4数据库用户表更新

注册成功的同时指定目录下会生成该用户名命名的文件夹,如图 5‑5所示。

5‑5生成用户文件夹

用户可以登录已经注册过的用户名进行登录,需要输入用户名和该用户名对应的密码才能登录成功,如图 5‑6所示。

5‑6用户登录成功

若用户输入不存在的用户名或错误的密码则会弹窗警告,如图 5‑7所示。

5‑7用户登录失败

登录成功后点击创建文件按钮,进行创建文件,如图 5‑8所示。

5‑8文件创建

文件创建成功后数据的文件表中会自动新增一条内容,包含用户名、文件名、文件可读属性、文件可写属性,且可读可写属性默认为1代表默认可读可写,用户目录下也会自动生成该文件如图 5‑9、图 5‑10所示。

5‑9创建文件后文件表更新

5‑10创建文件后用户目录下内容

点击写入文件按钮,对文件内容进行添加,如图 5‑11所示。

5‑11写入文件

点击读取按钮,读取文件内容,如图 5‑12所示。

5‑12读取文件

点击文件信息按钮,查看文件信息,如图 5‑13所示。

5‑13查看文件信息

点击更改属性按钮,更改文件属性,如图 5‑14所示。

5‑14更改文件属性

以取消可读属性为例,读取该文件会出现弹窗警告,如图 5‑15所示。

5‑15文件不可读

点击文件目录,会立刻显示该用户目录下的所有文件的文件名以及文件名对应的文件大小,如图 5‑16所示。

5‑16文件目录

点击删除文件按钮,文件成功删除后,用户目录下的文件会被删除数据库中文件表对应的内容也会删除,如图 5‑17、图 5‑18、图 5‑19所示。

5‑17删除文件

5‑18删除文件后文件表内容

5‑19删除文件后用户目录下内容

在用户操作界面中,运行写入文件模块时,若输入的文件名不在用户目录中时会弹窗警告。如图 5‑20所示。

5‑20写入文件时文件不存在

在用户操作界面中,运行读取文件模块时,若输入的文件名不在用户目录中时会弹窗警告。如图 5‑21所示。

5‑21读取文件时文件不存在

在用户操作界面中,运行删除文件模块时,若输入的文件名不在用户目录中时会弹窗警告。如所图 5‑22所示。

5‑22删除文件时文件不存在

在用户操作界面中,运行文件信息模块时,若输入的文件名不在用户目录中时会弹窗警告。如图 5‑23所示。

5‑23文件信息模块文件不存在

用户点击退出登录按钮就会返回登录界面,如图 5‑24所示。

5‑24退出用户操作界面后的登录界面

六、拓展

该简单二级文件系统还有很多优化空间,比如:

1.数据库保存的密码是明文,应该给数据库密码部分进行加密处理

2.没有创建子目录的功能

3.创建文件类型单一,只能创建txt文件

4.为了方便传值用户名采用全局变量存在一定的风险

还有很多细节方面没有优化,该篇文章只是简单记录一下本人对该项目的设计,拓展空间是非常非常大的,希望能给大家对这个项目提供了一点思路,感谢各位观看。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐