QT中级(6)基于QT的文件传输工具(2)

news2024/12/31 4:50:28

QT中级(6)基于QT的文件传输工具(2)

  • 本文实现第一步
  • 1 新增功能
  • 2 运行效果
  • 3 实现思路
  • 4 源代码

实现这个文件传输工具大概需要那几步?

  1. 实现多线程对文件的读写
  2. 实现TCP客户端和服务端
  3. 实现网络传输

书接上回:QT中级(5)多线程读取一个文件,并在另一个文件夹中合成这个文件(1)

本文实现第一步

1 新增功能

  1. 增加线程暂停和取消功能
  2. 增加意外关闭软件,再次打开可以重新续传
  3. 增加对线程的析构

2 运行效果

在这里插入图片描述

3 实现思路

  1. 通过创建标识符,控制线程的开始、暂停、取消(结束)
//线程工作状态
enum class TransferState {
    None,
    Running,
    Paused,
    Canceled,
    Unexpected
};
  1. 增加配置文件,记住软件运行的状态和一些文件相关信息
static QString configFilePath; //配置文件路径
static QString sourceFilePath; //源文件路径
static QString targetFilePath; //目标文件路径

static QString lastUnexpectedFileName;       //上次没有传输完成的源文件名字
static QString lastUnexpectedTargetFileName; //上次没有传输完成的目标文件名字
static quint64 unexpectedFileSize;           //上次文件已传输大小
static quint64 unexpectedFilePos;            //上次文件传输的位置
  1. 在主线程中的析构函数中增加对工作线程的析构,需要注意的是可以通过向工作线程中传入相应的状态,控制线程是否结束,如果强制关闭软件,而线程并不结束
MainWindow::~MainWindow()
{
    readThread->setStatus(AppConfig::TransferState::Unexpected);
    writeThread->setStatus(AppConfig::TransferState::Unexpected);
    threadPool->waitForDone();
    delete readThread;
    delete writeThread;
    delete ui;
}

4 源代码

源代码下载链接

work.h

#ifndef WORK_H
#define WORK_H

#include <QObject>
#include <QRunnable>
#include "appconfig.h"
#define DATA_SIZE 1024 * 1024

class ReadWork : public QObject,public QRunnable
{
    Q_OBJECT
public:
    explicit ReadWork(QObject *parent = nullptr);
    ~ReadWork();
    void run() override;

    void getFile(const QString &filePath);
    void setStatus(const AppConfig::TransferState &status);

signals:
    void sendData(const QByteArray &data);
    void sendFileInfo(const QString &fileInfo);
    void msgTips(const QString &msg);
    void updatePgBar(int num);
private:
    QString filePath;
    AppConfig::TransferState status;
};


class WriteWork : public QObject,public QRunnable
{
    Q_OBJECT
public:
    explicit WriteWork(QObject *parent = nullptr);
    ~WriteWork();
    void run() override;

    void getPath(const QString &filePath);
    void setStatus(const AppConfig::TransferState &status);

public slots:
    void getData(const QByteArray &data);
    void getFileInfo(const QString &data);
signals:
    void msgTips(const QString &msg);
    void updatePgBar(int num);
private:
    QString filePath;
    QString fileInfo;
    QList<QByteArray> arryList;
    AppConfig::TransferState status;
};

#endif // WORK_H

work.cpp

void ReadWork::run()
{
    //判断文件是否存在
    QFileInfo fileInfo(filePath);
    if(!fileInfo.exists())
    {
        emit msgTips("文件不存在!");
        return;
    }

    quint64 sourceFileSize = fileInfo.size();
    quint64 fileSize = (AppConfig::lastUnexpectedFileName.isEmpty())? 0 : AppConfig::unexpectedFileSize;//已经传输的字节
    quint64 filePos = AppConfig::unexpectedFilePos;
    //获得文件信息
    QString fileInfoStr = QString("%1|%2|%3|%4|%5")
            .arg(fileInfo.fileName()).arg(sourceFileSize).arg(fileInfo.absoluteFilePath())
            .arg(fileInfo.suffix()).arg(fileSize);

    emit sendFileInfo(fileInfoStr);
    emit msgTips(fileInfoStr);

    //打开文件
    QFile file(filePath);
    if(!file.open(QIODevice::ReadOnly))
    {
        emit msgTips(QString("文件打开失败!%1。").arg(file.errorString()));
        return;
    }


    QByteArray data;//存储发送的数据
    //移动文件指针
    file.seek(filePos);
    //读取文件
    while(!file.atEnd())
    {
        //取消
        if(status != AppConfig::TransferState::Canceled)
        {
            //意外关闭
            if(status == AppConfig::TransferState::Unexpected)
            {
                //记住文件名称
                AppConfig::lastUnexpectedFileName = filePath;
                //记住已经传输的字节
                AppConfig::unexpectedFileSize = fileSize;
                //记住文件指针的位置,方便下次传输
                AppConfig::unexpectedFilePos = file.pos();
                AppConfig::writeConfig();
                file.close();
                return;
            }

            //暂停
            if(status != AppConfig::TransferState::Paused)
            {
                int readSize = (sourceFileSize-file.pos()<DATA_SIZE)?(sourceFileSize-file.pos()):DATA_SIZE;
                data = file.read(readSize);
                fileSize += data.size();
                int pgBarStep = static_cast<int>(fileSize*100/sourceFileSize);
                emit updatePgBar(pgBarStep);
                emit sendData(data);
                QThread::msleep(100);
            }
            else
            {
                QThread::msleep(100);
            }//if(status != AppConfig::TransferState::Paused)
        }
        else
        {
            file.close();
            emit updatePgBar(0);
            emit msgTips("取消文件传输!");
            return;
        }


    }
    file.close();
    emit msgTips("读取完毕!");
}

写线程
void WriteWork::run()
{
    //如果文件路径为空,则使用当前应用程序所在路径
    if(filePath.isEmpty())
        filePath = QCoreApplication::applicationDirPath();
    //创建文件
    QFile file;
    quint64 sourceFileSize = 0;
    quint64 currentFileSize = 0;
    QString fileName="";

    //获取并解析文件信息
    if(fileInfo.isEmpty())
    {
        emit msgTips("未收到文件信息");
        return;
    }

    QStringList fileInfos = fileInfo.split("|");
    currentFileSize = fileInfos.last().toUInt();
    fileName = fileInfos.at(0);
    sourceFileSize = fileInfos.at(1).toInt();

    //判断文件是不是重传文件
    if(currentFileSize > 0)
    {
        fileName = AppConfig::lastUnexpectedTargetFileName;
        file.setFileName(fileName);
        file.rename(fileName.chopped(4));
    }
    else
    {
        //设置文件相关信息
        file.setFileName(filePath+"/"+fileName);
    }


    emit msgTips("设置文件路径和名称完毕!");

    //打开文件
    if(!file.open(QIODevice::ReadWrite))
    {
        emit msgTips("文件打开失败:"+file.errorString());
        return;
    }
    emit msgTips("文件打开成功!");

    //写入数据
    file.seek(currentFileSize);
    while(sourceFileSize-currentFileSize>0)
    {
        //意外关闭-等待旧数据处理完毕才能执行
        if(status == AppConfig::TransferState::Unexpected && arryList.isEmpty())
        {
            file.close();
            //更改文件名称-在文件后缀中增加.tmp
            if(file.fileName().count(".tmp")>0)
                return;
            file.rename(file.fileName()+".tmp");
            AppConfig::lastUnexpectedTargetFileName = file.fileName();
            AppConfig::writeConfig();
            return;
        }

        //取消
        if(status == AppConfig::TransferState::Canceled)
        {
            file.close();
            file.remove();
            arryList.clear();
            emit msgTips("文件取消传输");
            emit updatePgBar(0);

            if(!AppConfig::lastUnexpectedTargetFileName.isEmpty())
            {
                QFile::remove(AppConfig::lastUnexpectedTargetFileName);
                //重置
                AppConfig::lastUnexpectedFileName ="";
                AppConfig::lastUnexpectedTargetFileName ="";
                AppConfig::unexpectedFileSize = 0;
                AppConfig::unexpectedFilePos = 0;
                AppConfig::writeConfig();
            }
            return;
        }

        //按下暂停键,且数据处理完,才能暂停
        if(status == AppConfig::TransferState::Paused && arryList.isEmpty())
        {
            //暂停
            QThread::msleep(100);
            continue;
        }
        //防止出现新数据没到,但是旧数据已经处理完
        if(arryList.isEmpty())
            continue;
        QByteArray fileData = arryList.takeFirst();
        quint64 size = file.write(fileData,fileData.size());
        currentFileSize += size;
        int step = static_cast<int>((currentFileSize*100)/sourceFileSize) ;
        emit updatePgBar(step);
        QThread::msleep(100);
    }//while

    file.close();
    emit msgTips("写入完毕!");

    if(!AppConfig::lastUnexpectedTargetFileName.isEmpty())
    {
        QFile::remove(AppConfig::lastUnexpectedTargetFileName);
        //重置
        AppConfig::lastUnexpectedFileName ="";
        AppConfig::lastUnexpectedTargetFileName ="";
        AppConfig::unexpectedFileSize = 0;
        AppConfig::unexpectedFilePos = 0;
        AppConfig::writeConfig();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/380340.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

27k入职阿里测开岗那天,我哭了,这5个月付出的一切总算没有白费~

先说一下自己的个人情况&#xff0c;计算机专业&#xff0c;16年普通二本学校毕业&#xff0c;经历过一些失败的工作经历后&#xff0c;经推荐就进入了华为的测试岗&#xff0c;进去才知道是接了个外包项目&#xff0c;不太稳定的样子&#xff0c;可是刚毕业谁知道什么外包不外…

沸点 | 实时图数据库技术将赋能银行数字化转型——访同心尚科技总裁王昊

实时图数据库技术将赋能银行数字化转型 ——访同心尚科技总裁王昊 本报记者 赵萌 全国两会召开在即&#xff0c;近日&#xff0c;在多家媒体或研究机构的两会热点话题预测中&#xff0c;“科技创新”“数字经济”位列其中。如何更好发挥信息科技对支持实体经济发展的放大、叠加…

【运维有小邓】Oracle数据库审计

一些机构通常将客户记录、信用卡信息、财务明细之类的机密业务数据存储在Oracle数据库服务器中。这些数据存储库经常因为内部安全漏洞和外部安全漏洞而受到攻击。对这类敏感数据的任何损害都可能严重降低客户对机构的信任。因此&#xff0c;数据库安全性对于任何IT管理员来说都…

webpack.config.js与package.json文件的配置

path要使用绝对路径&#xff0c;通过每次复制文件位置非常麻烦且容易导致问题 使用node中的 写个包名跟入口名称&#xff0c;其他全部回车 此步完成后&#xff0c;自动生成一个package.json包 licence指的是开源&#xff0c;一般不写 安装文件夹需要的依赖 dirname是node自带…

图注意网络GAT理解及Pytorch代码实现【PyGAT代码详细注释】

文章目录GAT代码实现【PyGAT】GraphAttentionLayer【一个图注意力层实现】用上面实现的单层网络测试加入Multi-head机制的GAT对数据集Cora的处理csr_matrix()处理稀疏矩阵encode_onehot()对label编号build graph邻接矩阵构造GAT的推广GAT 题&#xff1a;Graph Attention Netwo…

Netty之ChannelFuture详解

目录 目标 Netty版本 Netty官方API 客户端如何与服务器建立连接&连接成功后的操作方式 实现 如何处理客户端与服务器连接关闭后的操作 正确关闭连接的方式 方法一 方法二 目标 了解Netty如何处理客户端与服务器之间的连接与关闭问题。 Netty版本 <dependency&…

Kafka系列之:Kafka生产者和消费者

Kafka系列之:Kafka生产者和消费者 一、Kafka生产者发送流程二、提高生产者吞吐量三、Kafka消费方式四、Kafka消费者总体工作流程五、按照时间消费Kafka Topic一、Kafka生产者发送流程 batch.size:只有数据积累到batch.size之后,sender才会发送数据,默认16K。linger.ms:如果…

预热:Eyeshot 2023 Beta 正式版不远 Eyeshot 2023 Fem

预热&#xff1a;Eyeshot 2023 Beta 离正式版不远 Eyeshot 2023 Fem 破解版 devDept Software 自豪地宣布推出新的Eyeshot 2023 Beta版本。 现在已经完成了几次迁移&#xff0c;我们有了一个最终的工作区架构&#xff0c;它不再需要设计/设计用户界面分离的对象。正如我们在迁移…

SMPL可视化大杀器,你并不需要下载SMPL就能可视化你的3D Pose

SMPL 是一种3D人体建模方法&#xff0c;现在几乎所有的元宇宙人体建模都是基于此类方法&#xff0c;包括但不限于元宇宙&#xff0c;自动驾驶等领域。它能估计出比较准确的人体3D姿态&#xff0c;得益于海量数据训练的人体3D先验。不仅仅是人体&#xff0c;包括手部&#xff0c…

【Windows应急响应】HW蓝队必备——开机启动项、临时文件、进程排查、计划任务排查、注册表排查、恶意进程查杀、隐藏账户、webshell查杀等

Windows应急响应应急响应的重要性开机启动项temp文件分析浏览器信息分析文件时间属性分析最近打开文件分析进程分析计划任务隐藏账户的发现添加与删除恶意进程发现及关闭补丁信息webshell查杀应急响应的重要性 近年来信息安全事件频发&#xff0c;信息安全的技能、人才需求大增…

linux + jenkins + svn + maven + node 搭建及部署springboot多模块前后端服务

linux搭建jenkins 基础准备 linux配置jdk、maven&#xff0c;配置系统配置文件 vi /etc/profile配置jdk、maven export JAVA_HOME/usr/java/jdk1.8.0_261-amd64 export CLASSPATH.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jarexport MAVEN_H…

【深入浅出 Yarn 架构与实现】4-6 RM 行为探究 - 申请与分配 Container

本小节介绍应用程序的 ApplicationMaster 在 NodeManager 成功启动并向 ResourceManager 注册后&#xff0c;向 ResourceManager 请求资源&#xff08;Container&#xff09;到获取到资源的整个过程&#xff0c;以及 ResourceManager 内部涉及的主要工作流程。 一、整体流程 …

吴恩达机器学习笔记——线性回归

1.模型描述有训练集数据房子面积和卖出的价钱&#xff0c;我们用这组数据来模拟特定面积的房子能够卖出的价钱。这是一个很明显的监督学习&#xff08;supervised learning&#xff09;的例子&#xff0c;因为我们的训练集里包含了正确的结果&#xff08;即房子的卖价&#xff…

非递归迭代实现二叉树前序,中序,后序遍历

文章目录1. 前序遍历2. 中序遍历3. 后序遍历1. 前序遍历 题目链接 解题思路&#xff1a; 非递归遍历一棵树有两点&#xff1a; 1.左路结点 2.左路结点的右子树 什么意思呢&#xff1f; 我们知道前序遍历是按照根&#xff0c;左子树&#xff0c;右子树来的。所以它是先根&…

js中的原型链

js中原型和原型链&#x1f61a; 1、为什么需要原型链&#xff1f;&#x1f923;&#x1f61a; 凡事都是有一定的需求和原因发展起来的&#xff0c;在ECMA中为什么要提出原型链这个概念呢&#xff1f; 我们知道&#xff0c;创建对象有两种方式。一种是通过字面量来创建&#…

科研 | 论文写作 | 最常用的LaTeX语法

最常用的LaTeX语法1. 行内公式2. 行间公式3. 下标4. 上标5. 公式编号6. 数学公式7. 根号和分式8. 上下标记9. 向量10. 积分、极限、求和、乘积11. 三圆点12. 重音符号13. 矩阵14. 小写希腊字母和大写希腊字母15. 公式组合16. 拆分单个公式1. 行内公式 格式&#xff1a;将公式编…

流计算框架storm概览

Attention: supervison 和 nimbus的状态都实时保存在zookeeper集群中和本地. Enchance, this means you can kill -9 Nimbus or the Supervisors and theyll start back up as nothing happened. Topologies 1. storm jar all-my-code.jar org.apache.storm.MyTopology a…

父类子类静态代码块、构造代码块、构造方法执行顺序

github:https://github.com/nocoders/java-everything.git 名词解释 静态代码块&#xff1a;java中使用static关键字修饰的代码块&#xff0c;每个代码块只会执行一次&#xff0c;JVM加载类时会执行静态代码块中的代码&#xff0c;静态代码块先于主方法执行。构造代码块&#…

[Java面经] 三年工作经验, 极兔一二面

极兔一二面面经: 1. mysql的acid怎么实现的 这一点先回答ACID分别是A(原子性),C(一致性),I(隔离性),D(持久性), 其中持久性是数据库落磁盘的操作,无需额外实现. 隔离性是通过事务的隔离级别来实现, MySQL默认的隔离级别是RR(可重复读), 虽然上面还有一层Serializable(串行化…

如何在canvas中模拟css的背景图片样式

笔者开源了一个Web思维导图mind-map&#xff0c;最近在优化背景图片效果的时候遇到了一个问题&#xff0c;页面上展示时背景图片是通过css使用background-image渲染的&#xff0c;而导出的时候实际上是绘制到canvas上导出的&#xff0c;那么就会有个问题&#xff0c;css的背景图…