当前位置: 首页 > news >正文

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

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 * 1024class 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();//更改文件名称-在文件后缀中增加.tmpif(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);}//whilefile.close();emit msgTips("写入完毕!");if(!AppConfig::lastUnexpectedTargetFileName.isEmpty()){QFile::remove(AppConfig::lastUnexpectedTargetFileName);//重置AppConfig::lastUnexpectedFileName ="";AppConfig::lastUnexpectedTargetFileName ="";AppConfig::unexpectedFileSize = 0;AppConfig::unexpectedFilePos = 0;AppConfig::writeConfig();}
}

相关文章:

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

QT中级&#xff08;6&#xff09;基于QT的文件传输工具&#xff08;2&#xff09;本文实现第一步1 新增功能2 运行效果3 实现思路4 源代码实现这个文件传输工具大概需要那几步&#xff1f;实现多线程对文件的读写实现TCP客户端和服务端实现网络传输 书接上回&#xff1a;QT中级…...

【Linux】工具(3)——gcc/g++

咱们继续进阶&#xff0c;接下来进入到Linux工具中gcc和g的学习在本章博客正式开始介绍之前&#xff0c;我们先要弄清楚程序是怎么翻译的&#xff1a;C语言程序环境一、什么是gcc/g&#x1f4cc;gcc是一个c编译器&#xff0c; g是c编译器。我们根据代码的后缀名来判断用哪个编译…...

Android文件选择器

使用方法:在里层的build.grade的dependency里面加入: implementation com.leon:lfilepickerlibrary:1.8.0 引用https://github.com/leonHua/LFilePicker/blob/master/README_CH.md#lfilepicker LFilePicker 说明:如果发现应用名称被修改,可以参考issues#26 查看解决方案,或…...

《MySql学习》 Select 查询语句慢的非性能原因

一.查询被阻塞 A会话执行 查询操作&#xff0c;长时间没有返回信息,此时我们就可以去排查一下是否是被阻塞了 select * from words 被阻塞的原因有很多&#xff0c;首先列举第一种情况 1.等MDL锁 当我们执行DDL语句时&#xff0c;会自动给表加上MDL写锁。当执行DML和DQL时&…...

Vue组件间通信方式超详细(父传子、父传后代、子传父、后代传父、兄弟组件传值)

一、父传子、父传后代 方式一&#xff1a;子通过props来接收 父组件&#xff1a;父组件引入子组件时&#xff0c;通过<child :parentValue "parentValue"></child>子组件传值。 备注&#xff1a;这种方式父传值很方便&#xff0c;但是传递给后代组件不…...

【ES】Elasticsearch-深入理解索引原理

文章目录Elasticsearch-深入理解索引原理读操作更新操作SHARD不变性动态更新索引删除和更新实时索引更新持久化Segment合并近实时搜索&#xff0c;段数据刷新&#xff0c;数据可见性更新和事务日志更新索引并且将改动提交修改Searcher对象默认的更新时间Elasticsearch-深入理解…...

pdf压缩文件大小的方法是什么?word文件怎么批量转换成pdf格式?

大家在存储文件时&#xff0c;通常会遇到一些较大的文件&#xff0c;这时需要对其进行压缩处理。下面介绍一下如何压缩PDF文件大小以及批量转换Word文件为PDF格式。pdf压缩文件大小的方法是什么?1.打开小圆象PDF转换器&#xff0c;选择“PDF压缩”功能。2.在“PDF压缩”界面中…...

论文阅读——FECANet:应用特征增强的上下文感知小样本语义分割网络

代码&#xff1a;NUST-Machine-Intelligence-Laboratory/FECANET (github.com) 文章地址&#xff1a;地址 文章名称&#xff1a;FECANet: Boosting Few-Shot Semantic Segmentation with Feature-Enhanced Context-Aware Network 摘要 Few-shot semantic segmentation 是学习…...

数组模拟常见数据结构

我们来学习一下用数组模拟常见的数据结构&#xff1a;单链表&#xff0c;双链表&#xff0c;栈&#xff0c;队列。用数组模拟这些常见的数据结构&#xff0c;需要我们对这些数据结构有一定的了解哈。单链表请参考&#xff1a;http://t.csdn.cn/SUv8F 用数组模拟实现比STL要快&a…...

ADC0832的AD模数转换原理及编程

✅作者简介&#xff1a;嵌入式领域优质创作者&#xff0c;博客专家 ✨个人主页&#xff1a;咸鱼弟 &#x1f525;系列专栏&#xff1a;单片机设计专栏 &#x1f4c3;推荐一款求职面试、刷题神器&#x1f449;注册免费刷题 目录 一、描述 二、模数转换原理&#xff1a; 三、…...

【工具插件类教学】UnityPackageManager私人定制资源工具包

目录 一.UnityPackageManager的介绍 二.package包命名 三.包的布局 四.生成清单文件 五.制作package内功能 六.为您的软件包撰写文档 1.信息的结构 2.文档格式 七.提交上传云端仓库 1.生成程序集文件 2.上传至云端仓库 八.下载使用package包 1.获取包的云端路径 …...

【软件测试】2023年了还不会接口测试?老鸟总结接口测试面试谁还敢说我不会......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 测试面试&#xff0…...

类Vuex轻量级状态管理实现

引用自 摸鱼wiki 1. vuex vuex是一个前端广泛流行的状态管理库&#xff0c;主要由以下几大模块组成&#xff1a; state&#xff1a;状态存储getter&#xff1a;属性访问器mutation&#xff1a;可以理解为一个同步的原子性事务&#xff0c;修改state状态action&#xff1a;触发…...

Java 基本数据类型

Java基本数据类型是Java编程语言中最基本的数据类型&#xff0c;包括整型、浮点型、字符型、布尔型和空类型。本文将详细介绍Java基本数据类型的作用和在实际工作中的用途。 整型&#xff08;int、long、short、byte&#xff09; 整型是Java中最常见的基本数据类型&#xff0…...

全网资料最全Java数据结构与算法-----算法分析

算法分析 研究算法的最终目的就是如何花更少的时间&#xff0c;如何占用更少的内存去完成相同的需求&#xff0c;并且也通过案例演示了不同算法之间时间耗费和空间耗费上的差异&#xff0c;但我们并不能将时间占用和空间占用量化&#xff0c;因此&#xff0c;接下来我们要学习…...

【封装xib补充 Objective-C语言】

一、那么首先,咱们就从这个结果来分析 1.就不给大家一步一步分析了,直接分析我们这里怎么想的, 首先,我们看到这样的一个界面,我们想,这些应用数据是不是来源于一个plist文件吧, 所以说,我们首先要,第一步,要懒加载,把这个plist文件中的数据,加载起来, 那么,因…...

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…...

VBA之正则表达式(41)-- 快速标记两个星号之后的字符

实例需求&#xff1a;工作表中的数据保存在A列~G列&#xff0c;现需要识别D列中包含超过两个星号的内容&#xff0c;并将第3个星号及其之后的字符设置为红色字体&#xff0c;如图所示。 示例代码如下。 Sub Demo1()Dim objRegExp As ObjectDim objMatch As ObjectDim strMatch…...

VMware16安装MacOS【详细教程】

安装VMware workstation 双击安装包&#xff0c;然后一直下一步就行了。 进行VMware安装&#xff0c;一直 下一步 在输入产品密钥这一步&#xff0c;如果有查找到可用密钥就填进去&#xff0c;没有就跳过&#xff0c;进入软件后也能输入密钥的。 输入密钥。 最后一步&#xff…...

Netty学习(一):Netty概述

一、原生NIO存在的问题 NIO 的类库和API繁杂&#xff0c;使用麻烦:需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。需要具备其他的额外技能:要熟悉Java 多线程编程&#xff0c;因为NIO编程涉及到Reactor 模式&#xff0c;你必须对多线程和网络编程…...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

spring Security对RBAC及其ABAC的支持使用

RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型&#xff0c;它将权限分配给角色&#xff0c;再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...

电脑桌面太单调,用Python写一个桌面小宠物应用。

下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡&#xff0c;可以响应鼠标点击&#xff0c;并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...

DAY 45 超大力王爱学Python

来自超大力王的友情提示&#xff1a;在用tensordoard的时候一定一定要用绝对位置&#xff0c;例如&#xff1a;tensorboard --logdir"D:\代码\archive (1)\runs\cifar10_mlp_experiment_2" 不然读取不了数据 知识点回顾&#xff1a; tensorboard的发展历史和原理tens…...