QT中级(6)基于QT的文件传输工具(2)
- 本文实现第一步
- 1 新增功能
- 2 运行效果
- 3 实现思路
- 4 源代码
实现这个文件传输工具大概需要那几步?
- 实现多线程对文件的读写
- 实现TCP客户端和服务端
- 实现网络传输
书接上回:QT中级(5)多线程读取一个文件,并在另一个文件夹中合成这个文件(1)
本文实现第一步
1 新增功能
- 增加线程暂停和取消功能
- 增加意外关闭软件,再次打开可以重新续传
- 增加对线程的析构
2 运行效果
3 实现思路
- 通过创建标识符,控制线程的开始、暂停、取消(结束)
//线程工作状态
enum class TransferState {
None,
Running,
Paused,
Canceled,
Unexpected
};
- 增加配置文件,记住软件运行的状态和一些文件相关信息
static QString configFilePath; //配置文件路径
static QString sourceFilePath; //源文件路径
static QString targetFilePath; //目标文件路径
static QString lastUnexpectedFileName; //上次没有传输完成的源文件名字
static QString lastUnexpectedTargetFileName; //上次没有传输完成的目标文件名字
static quint64 unexpectedFileSize; //上次文件已传输大小
static quint64 unexpectedFilePos; //上次文件传输的位置
- 在主线程中的析构函数中增加对工作线程的析构,需要注意的是可以通过向工作线程中传入相应的状态,控制线程是否结束,如果强制关闭软件,而线程并不结束
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();
}
}