添加文件夹传输进度更新。

This commit is contained in:
2026-03-24 22:25:00 +08:00
parent 5843332980
commit 0e397ace7d
12 changed files with 290 additions and 165 deletions

View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16)
project(frelay VERSION 0.2.6 LANGUAGES CXX)
project(frelay VERSION 0.2.7 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

View File

@@ -142,6 +142,10 @@ void ClientCore::handleAsk(QSharedPointer<FrameBuffer> frame)
}
return;
}
// 这个请求的处理可能是耗时的,需要开线程处理。
if (msg.command == STRMSG_AC_ALL_DIRFILES) {
msg.command = STRMSG_AC_ANSWER_ALL_DIRFILES;
}
// 未知信息
qWarning() << QString(tr("未知询问信息类型:%1")).arg(msg.command);
}
@@ -414,3 +418,96 @@ void WaitThread::interrupCheck()
emit sigCheckOver();
}
}
WaitOper::WaitOper(QObject* parent) : WaitThread(parent)
{
}
void WaitOper::run()
{
isAlreadyInter_ = false;
infoMsg_.msg = STR_NONE;
isRun_ = true;
recvMsg_ = false;
InfoMsg msg;
msg.command = sendStrType_;
msg.fromPath = stra_;
msg.toPath = strb_;
msg.type = type_;
auto f = cli_->GetBuffer<InfoMsg>(msg, FBT_MSGINFO_ASK, cli_->GetRemoteID());
if (!ClientCore::syncInvoke(cli_, f)) {
auto errMsg = QString(tr("向%1发送%2请求失败。")).arg(cli_->GetRemoteID()).arg(sendStrType_);
emit sigCheckOver();
qCritical() << errMsg;
return;
}
while (isRun_) {
QThread::msleep(1);
if (isAlreadyInter_) {
qInfo() << tr("线程中断文件操作等待......");
return;
}
if (!recvMsg_) {
continue;
}
break;
}
isAlreadyInter_ = true;
emit sigCheckOver();
auto n = QString(tr("向%1的请求%2处理结束。")).arg(cli_->GetRemoteID()).arg(sendStrType_);
qInfo() << n;
}
void WaitOper::SetType(const QString& sendType, const QString& ansType)
{
sendStrType_ = sendType;
ansStrType_ = ansType;
}
void WaitOper::SetPath(const QString& stra, const QString& strb, const QString& type)
{
stra_ = stra;
strb_ = strb;
type_ = type;
}
InfoMsg WaitOper::GetMsg() const
{
return infoMsg_;
}
void WaitOper::interrupCheck()
{
qWarning() << QString(tr("中断请求处理%1......")).arg(sendStrType_);
WaitThread::interrupCheck();
}
void WaitOper::recvFrame(QSharedPointer<FrameBuffer> frame)
{
InfoMsg info = infoUnpack<InfoMsg>(frame->data);
if (info.command == ansStrType_) {
infoMsg_ = info;
recvMsg_ = true;
return;
}
auto n = tr("收到未知Oper的回复信息:") + info.command;
qInfo() << n;
}
WaitOperOwn::WaitOperOwn(QObject* parent) : WaitThread(parent)
{
}
void WaitOperOwn::run()
{
if (func_) {
func_();
}
emit sigOver();
}
void WaitOperOwn::recvFrame(QSharedPointer<FrameBuffer> frame)
{
}

View File

@@ -123,7 +123,7 @@ public:
LocalFile localFile_;
};
// Socket Worker Thread
// 工作线程。
class SocketWorker : public QThread
{
Q_OBJECT
@@ -139,7 +139,7 @@ private:
ClientCore* core_{};
};
// HeatBeat to Server
// 心跳包线程。
class HeatBeat : public QThread
{
Q_OBJECT
@@ -159,6 +159,7 @@ private:
ClientCore* core_{};
};
// 耗时操作线程基本框架。
class WaitThread : public QThread
{
Q_OBJECT
@@ -183,4 +184,48 @@ protected:
ClientCore* cli_{};
};
// 等待对方应答的等待线程。
class WaitOper : public WaitThread
{
public:
WaitOper(QObject* parent = nullptr);
public:
void run() override;
void SetType(const QString& sendType, const QString& ansType);
void SetPath(const QString& stra, const QString& strb, const QString& type);
InfoMsg GetMsg() const;
void interrupCheck() override;
void recvFrame(QSharedPointer<FrameBuffer> frame) override;
private:
bool recvMsg_{};
InfoMsg infoMsg_{};
QString sendStrType_{};
QString ansStrType_{};
QString stra_;
QString strb_;
QString type_;
};
// 等待自己耗时操作的线程。
class WaitOperOwn : public WaitThread
{
Q_OBJECT
public:
WaitOperOwn(QObject* parent = nullptr);
signals:
void sigOver();
public:
void run() override;
void recvFrame(QSharedPointer<FrameBuffer> frame) override;
public:
InfoMsg infoMsg_{};
std::function<void()> func_;
};
#endif // CLIENTCORE_H

View File

@@ -398,7 +398,25 @@ void FileTrans::SendFile(const QSharedPointer<DoTransTask>& task)
QMutexLocker locker(&sthMut_);
upTasks_[task->task.localId] = sendThread;
sendThread->run();
// 2026-03-24 找了一个可能卡顿的原因。
/*
在 Qt 的 QThread类中,start()和 run()有本质区别:
start()- 创建新线程 sendThread->start(); // ✅ 正确
启动一个新线程,在新线程中执行 run()方法,线程有自己的事件循环(如果调用 exec())
自动处理线程同步和资源管理信号/槽可以跨线程工作
run()- 只是普通方法调用 sendThread->run(); // ❌ 错误用法
在当前线程中直接调用这个方法,没有创建新线程阻塞当前线程直到 run()返回
对象的信号/槽仍然在原始线程上下文中
*/
// sendThread->run();
sendThread->start();
}
SendThread::SendThread(ClientCore* clientCore) : cliCore_(clientCore)
@@ -442,7 +460,7 @@ void SendThread::run()
}
task_->tranSize += frame->data.size();
// 关键点:这里不调用,无法中途收到别人发的数据。
QCoreApplication::processEvents();
// QCoreApplication::processEvents();
}
qInfo() << QString(tr("结束发送文件:%1")).arg(task_->file.fileName());

View File

@@ -511,6 +511,34 @@ void FileManager::UpDown()
QMessageBox::information(this, tr("提示"), tr("请选择完整的行。"));
return;
}
//
WaitOperOwn wo(this);
auto getAllItems = [this, &datas, &wo]() {
wo.infoMsg_.list.clear();
for (int i = 0; i < (datas.size() / 5); ++i) {
const auto& curType = datas[i * 5 + 3]->text();
if (curType == "File") {
wo.infoMsg_.list << datas[i * 5 + 1]->text();
} else {
QVector<QString> fs;
DirFileHelper::GetAllFiles(datas[i * 5 + 1]->text(), fs);
if (!fs.isEmpty()) {
wo.infoMsg_.list << fs;
}
}
}
};
wo.func_ = getAllItems;
LoadingDialog checking(this);
checking.setTipsText("正在获取文件列表...");
checking.setCanCancel(false);
connect(&wo, &WaitOperOwn::sigOver, &checking, &LoadingDialog::cancelBtnClicked);
wo.start();
checking.exec();
QVector<TransTask> tasks;
for (int i = 0; i < (datas.size() / 5); ++i) {
if (datas[i * 5 + 3]->text() != "File") {
@@ -569,7 +597,7 @@ void FileManager::OperNewFolder()
if (!isRemote_) {
QString ret = Util::NewDir(folder);
if (ret.isEmpty()) {
//QMessageBox::information(this, tr("提示"), tr("创建%1成功").arg(folder));
// QMessageBox::information(this, tr("提示"), tr("创建%1成功").arg(folder));
DirFileInfo nf;
nf.size = 0;
nf.type = Dir;
@@ -609,7 +637,7 @@ void FileManager::OperNewFolder()
if (msg.msg == STR_NONE || !msg.msg.isEmpty()) {
QMessageBox::information(this, tr("提示"), QString(tr("新建失败=>%1")).arg(msg.msg));
} else {
//QMessageBox::information(this, tr("提示"), QString(tr("新建%1成功。")).arg(folder));
// QMessageBox::information(this, tr("提示"), QString(tr("新建%1成功。")).arg(folder));
DirFileInfo nf;
nf.size = 0;
nf.type = Dir;
@@ -642,7 +670,7 @@ void FileManager::OperDelete()
if (!isRemote_) {
QString ret = Util::Delete(name);
if (ret.isEmpty()) {
//QMessageBox::information(this, tr("提示"), tr("删除成功"));
// QMessageBox::information(this, tr("提示"), tr("删除成功"));
int row = datas[0]->row();
ui->tableWidget->removeRow(row);
} else {
@@ -676,7 +704,7 @@ void FileManager::OperDelete()
if (msg.msg == STR_NONE || !msg.msg.isEmpty()) {
QMessageBox::information(this, tr("提示"), QString(tr("删除失败=>%1")).arg(msg.msg));
} else {
//QMessageBox::information(this, tr("提示"), QString(tr("删除成功。")));
// QMessageBox::information(this, tr("提示"), QString(tr("删除成功。")));
ui->tableWidget->removeRow(datas[0]->row());
}
}
@@ -825,80 +853,3 @@ void FileManager::doubleClick(int row, int column)
QString np = dir.filePath(item->text());
fileHelper_->GetDirFile(np);
}
WaitOper::WaitOper(QObject* parent) : WaitThread(parent)
{
}
void WaitOper::run()
{
isAlreadyInter_ = false;
infoMsg_.msg = STR_NONE;
isRun_ = true;
recvMsg_ = false;
InfoMsg msg;
msg.command = sendStrType_;
msg.fromPath = stra_;
msg.toPath = strb_;
msg.type = type_;
auto f = cli_->GetBuffer<InfoMsg>(msg, FBT_MSGINFO_ASK, cli_->GetRemoteID());
if (!ClientCore::syncInvoke(cli_, f)) {
auto errMsg = QString(tr("向%1发送%2请求失败。")).arg(cli_->GetRemoteID()).arg(sendStrType_);
emit sigCheckOver();
qCritical() << errMsg;
return;
}
while (isRun_) {
QThread::msleep(1);
if (isAlreadyInter_) {
qInfo() << tr("线程中断文件操作等待......");
return;
}
if (!recvMsg_) {
continue;
}
break;
}
isAlreadyInter_ = true;
emit sigCheckOver();
auto n = QString(tr("向%1的请求%2处理结束。")).arg(cli_->GetRemoteID()).arg(sendStrType_);
qInfo() << n;
}
void WaitOper::SetType(const QString& sendType, const QString& ansType)
{
sendStrType_ = sendType;
ansStrType_ = ansType;
}
void WaitOper::SetPath(const QString& stra, const QString& strb, const QString& type)
{
stra_ = stra;
strb_ = strb;
type_ = type;
}
InfoMsg WaitOper::GetMsg() const
{
return infoMsg_;
}
void WaitOper::interrupCheck()
{
qWarning() << QString(tr("中断请求处理%1......")).arg(sendStrType_);
WaitThread::interrupCheck();
}
void WaitOper::recvFrame(QSharedPointer<FrameBuffer> frame)
{
InfoMsg info = infoUnpack<InfoMsg>(frame->data);
if (info.command == ansStrType_) {
infoMsg_ = info;
recvMsg_ = true;
return;
}
auto n = tr("收到未知Oper的回复信息:") + info.command;
qInfo() << n;
}

View File

@@ -28,29 +28,6 @@ enum class SortMethod {
SMD_BY_SIZE_ASC,
};
class WaitOper : public WaitThread
{
public:
WaitOper(QObject* parent = nullptr);
public:
void run() override;
void SetType(const QString& sendType, const QString& ansType);
void SetPath(const QString& stra, const QString& strb, const QString& type);
InfoMsg GetMsg() const;
void interrupCheck() override;
void recvFrame(QSharedPointer<FrameBuffer> frame) override;
private:
bool recvMsg_{};
InfoMsg infoMsg_{};
QString sendStrType_{};
QString ansStrType_{};
QString stra_;
QString strb_;
QString type_;
};
class FileManager : public QWidget
{
Q_OBJECT

View File

@@ -7,7 +7,8 @@
## 问题清单:
| 序号 | 类型 | 内容 | 说明 | 基于版本 | 完成版本 |
| :--: | :--: | ------------------------------------------------------------ | :----: | :------: | :------: |
| :--: | :--: | ------------------------------------------------------------ | :------------: | :------: | :------: |
| 52 | 问题 | 发送大文件时,UI有不流畅现象。 | 完成版本待观察 | 0.2.6 | |
| 51 | 问题 | 按照类型传输时,类型重复的问题修正 | | 0.2.5 | 0.2.6 |
| 50 | 功能 | 添加日志内容清空按钮。 | | 0.2.4 | 0.2.5 |
| 49 | 功能 | 对照传输支持按照类型传输。 | | 0.2.4 | 0.2.5 |

View File

@@ -102,6 +102,9 @@ enum FileCheckState {
#define STRMSG_AC_UP "upAction"
#define STRMSG_AC_DOWN "downAction"
#define STRMSG_AC_ALL_DIRFILES "askAllDirFiles"
#define STRMSG_AC_ANSWER_ALL_DIRFILES "answerAllDirFiles"
#define STRMSG_ST_FILEEXIT "fileExist"
#define STRMSG_ST_FILENOEXIT "fileNotExist"
#define STRMSG_ST_DIREXIT "dirExist"

View File

@@ -1,6 +1,7 @@
#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>
#include <Util.h>
void DateTimeTest()
{
@@ -14,5 +15,8 @@ int main(int argc, char** argv)
qDebug() << "Running...";
DateTimeTest();
QVector<QString> files;
DirFileHelper::GetAllFiles("D:/备份软件", files);
return app.exec();
}

View File

@@ -21,4 +21,4 @@ add_executable(frelayTest ${MSOURCES})
target_link_libraries(frelayTest PRIVATE Protocol Util)
add_executable(frelayBaseTest BaseTest.cpp)
target_link_libraries(frelayBaseTest Qt${QT_VERSION_MAJOR}::Core)
target_link_libraries(frelayBaseTest Qt${QT_VERSION_MAJOR}::Core Util Protocol)

View File

@@ -3,6 +3,7 @@
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QFileDevice>
#include <QFileInfo>
#include <QMutex>
@@ -190,6 +191,33 @@ QString DirFileHelper::GetErr() const
return QString();
}
bool DirFileHelper::GetAllFiles(const QString& rootPath, QVector<QString>& files)
{
QDir rootDir(rootPath);
if (!rootDir.exists()) {
qWarning() << "目录不存在:" << rootPath;
return false;
}
QString absoluteRootPath = rootDir.absolutePath();
QDirIterator it(absoluteRootPath, QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
while (it.hasNext()) {
it.next();
QFileInfo fileInfo = it.fileInfo();
if (fileInfo.isFile()) {
QString absoluteFilePath = fileInfo.absoluteFilePath();
QString relativePath = absoluteFilePath.mid(absoluteRootPath.length());
if (relativePath.startsWith('/') || relativePath.startsWith('\\')) {
relativePath = relativePath.mid(1);
}
files.append(relativePath);
}
}
return true;
}
DirFileHelper::DirFileHelper(QObject* parent) : QObject(parent)
{
}

View File

@@ -67,6 +67,7 @@ public:
public:
QString GetErr() const;
static bool GetAllFiles(const QString& rootPath, QVector<QString>& files);
signals:
void sigHome(const QString& path, const QVector<QString>& drivers);