diff --git a/ClientCore/ClientCore.cpp b/ClientCore/ClientCore.cpp index 60e6f58..dc79705 100644 --- a/ClientCore/ClientCore.cpp +++ b/ClientCore/ClientCore.cpp @@ -12,6 +12,9 @@ void ClientCore::Instance() socket_ = new QTcpSocket(this); connect(socket_, &QTcpSocket::readyRead, this, &ClientCore::onReadyRead); connect(socket_, &QTcpSocket::disconnected, this, &ClientCore::onDisconnected); + clearWaitTimer_ = new QTimer(this); + clearWaitTimer_->setInterval(10000); + connect(clearWaitTimer_, &QTimer::timeout, this, [this]() { clearWaitTask(); }); } ClientCore::~ClientCore() @@ -145,11 +148,79 @@ void ClientCore::handleAsk(QSharedPointer frame) // 这个请求的处理可能是耗时的,需要开线程处理。 if (msg.command == STRMSG_AC_ALL_DIRFILES) { msg.command = STRMSG_AC_ANSWER_ALL_DIRFILES; + QMutexLocker locker(&waitTaskMut_); + if (waitTask_.contains(frame->fid)) { + msg.msg = STRMSG_ST_COMMAND_ALREADY_RUNNING; + if (!Send(msg, FBT_MSGINFO_ANSWER, frame->fid)) { + auto logMsg = tr("给") + frame->fid + tr("返回获取文件列表结果消息失败。"); + qCritical() << logMsg; + return; + } + } else { + waitTask_[frame->fid] = WaitTask(); + auto& wt = waitTask_[frame->fid]; + QString fid = frame->fid; + wt.wo = new WaitOperOwn(this); + wt.wo->SetClient(this); + wt.wo->fid = fid; + wt.wo->infoMsg_ = msg; + wt.wo->func_ = [this, &wt, fid]() { + auto& infoMsg = wt.wo->infoMsg_; + infoMsg.command = STRMSG_AC_ANSWER_ALL_DIRFILES; + bool success = false; + //infoMsg.infos.clear(); + for (auto& item : infoMsg.infos.keys()) { + auto fullDir = Util::Join(infoMsg.fst.root, item); + if (!DirFileHelper::GetAllFiles(fullDir, infoMsg.list)) { + success = false; + break; + } + auto& vec = infoMsg.infos[item]; + for (const auto& dd : std::as_const(infoMsg.list)) { + FileStruct fst; + fst.root = infoMsg.fst.root; + fst.mid = item; + fst.relative = dd; + vec.push_back(fst); + } + } + return success; + }; + wt.wo->start(); + } + return; } // 未知信息 qWarning() << QString(tr("未知询问信息类型:%1")).arg(msg.command); } +void ClientCore::clearWaitTask() +{ + QMutexLocker locker(&waitTaskMut_); + QList completedTasks; + + for (auto it = waitTask_.begin(); it != waitTask_.end(); ++it) { + WaitTask& task = it.value(); + if (task.wo && task.wo->isFinished()) { + completedTasks.append(it.key()); + } + } + + for (const QString& taskId : completedTasks) { + auto it = waitTask_.find(taskId); + if (it != waitTask_.end()) { + WaitTask& task = it.value(); + if (task.wo) { + task.wo->wait(); + delete task.wo; + task.wo = nullptr; + } + waitTask_.erase(it); + qDebug() << "清理完成的任务:" << taskId; + } + } +} + void ClientCore::UseFrame(QSharedPointer frame) { switch (frame->type) { @@ -430,13 +501,12 @@ void WaitOper::run() isRun_ = true; recvMsg_ = false; - InfoMsg msg; - msg.command = sendStrType_; - msg.fromPath = stra_; - msg.toPath = strb_; - msg.type = type_; + infoMsg_.command = sendStrType_; + infoMsg_.fromPath = stra_; + infoMsg_.toPath = strb_; + infoMsg_.type = type_; - auto f = cli_->GetBuffer(msg, FBT_MSGINFO_ASK, cli_->GetRemoteID()); + auto f = cli_->GetBuffer(infoMsg_, FBT_MSGINFO_ASK, cli_->GetRemoteID()); if (!ClientCore::syncInvoke(cli_, f)) { auto errMsg = QString(tr("向%1发送%2请求失败。")).arg(cli_->GetRemoteID()).arg(sendStrType_); emit sigCheckOver(); @@ -473,7 +543,12 @@ void WaitOper::SetPath(const QString& stra, const QString& strb, const QString& type_ = type; } -InfoMsg WaitOper::GetMsg() const +InfoMsg WaitOper::GetMsgConst() const +{ + return infoMsg_; +} + +InfoMsg& WaitOper::GetMsgRef() { return infoMsg_; } @@ -502,12 +577,19 @@ WaitOperOwn::WaitOperOwn(QObject* parent) : WaitThread(parent) void WaitOperOwn::run() { + auto execRet = false; if (func_) { - func_(); + execRet = func_(); + } + if (!fid.isEmpty()) { + if (!cli_->syncInvoke(cli_, cli_->GetBuffer(infoMsg_, FBT_MSGINFO_ANSWER, fid))) { + qCritical() << QString(tr("向%1发送%2请求失败。")).arg(fid).arg(infoMsg_.command); + } } emit sigOver(); } void WaitOperOwn::recvFrame(QSharedPointer frame) { + qDebug() << "不应该被调用的地方:" << __FUNCTION__; } diff --git a/ClientCore/ClientCore.h b/ClientCore/ClientCore.h index be50990..72ec5f3 100644 --- a/ClientCore/ClientCore.h +++ b/ClientCore/ClientCore.h @@ -15,8 +15,14 @@ #include #include #include +#include #include +class WaitOperOwn; +struct WaitTask { + QString id; + WaitOperOwn* wo; +}; class ClientCore : public QObject { Q_OBJECT @@ -93,6 +99,7 @@ private: void onReadyRead(); void onDisconnected(); void handleAsk(QSharedPointer frame); + void clearWaitTask(); private: void UseFrame(QSharedPointer frame); @@ -121,6 +128,10 @@ public: bool connected_{false}; LocalFile localFile_; + + QTimer* clearWaitTimer_{}; + QMutex waitTaskMut_; + QMap waitTask_; }; // 工作线程。 @@ -194,7 +205,8 @@ 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; + InfoMsg GetMsgConst() const; + InfoMsg& GetMsgRef(); void interrupCheck() override; void recvFrame(QSharedPointer frame) override; @@ -224,8 +236,9 @@ public: void recvFrame(QSharedPointer frame) override; public: + QString fid; InfoMsg infoMsg_{}; - std::function func_; + std::function func_; }; #endif // CLIENTCORE_H \ No newline at end of file diff --git a/Gui/Control/FileControl.cpp b/Gui/Control/FileControl.cpp index d5ad21e..85bc7a2 100644 --- a/Gui/Control/FileControl.cpp +++ b/Gui/Control/FileControl.cpp @@ -511,60 +511,103 @@ void FileManager::UpDown() QMessageBox::information(this, tr("提示"), tr("请选择完整的行。")); return; } + /* + 要注意这一块的逻辑,本软件的所讲的【上传】【下载】都是针对本地。 + 这里的任务拼接和 DropEvent 有所不同, + DropEvent 是接收方负责拼接任务,但是这里是发送方拼接任务。 + + 所以这里的拼接逻辑需要注意。 + */ + QVector resultFiles; + + if (isRemote_) { + // 远程等待别人。 + WaitOper wi(this); + wi.SetClient(cliCore_); + wi.SetType(STRMSG_AC_ALL_DIRFILES, STRMSG_AC_ANSWER_ALL_DIRFILES); + auto& infoMsg = wi.GetMsgRef(); + infoMsg.infos.clear(); + infoMsg.fst.root = GlobalData::Ins()->GetRemoteRoot(); - // - WaitOperOwn wo(this); - auto getAllItems = [this, &datas, &wo]() { - wo.infoMsg_.list.clear(); for (int i = 0; i < (datas.size() / 5); ++i) { + FileStruct fst; + fst.root = GlobalData::Ins()->GetRemoteRoot(); + fst.mid = datas[i * 5 + 1]->text(); const auto& curType = datas[i * 5 + 3]->text(); if (curType == "File") { - wo.infoMsg_.list << datas[i * 5 + 1]->text(); + fst.relative = datas[i * 5 + 1]->text(); + resultFiles << fst; } else { - QVector fs; - DirFileHelper::GetAllFiles(datas[i * 5 + 1]->text(), fs); - if (!fs.isEmpty()) { - wo.infoMsg_.list << fs; - } + infoMsg.infos[datas[i * 5 + 1]->text()] = QVector{}; } } - }; - wo.func_ = getAllItems; - LoadingDialog checking(this); - checking.setTipsText("正在获取文件列表..."); - checking.setCanCancel(false); - connect(&wo, &WaitOperOwn::sigOver, &checking, &LoadingDialog::cancelBtnClicked); + LoadingDialog checking(this); + checking.setTipsText("正在等待对方获取文件列表..."); + connect(&wi, &WaitOper::sigCheckOver, &checking, &LoadingDialog::cancelBtnClicked); + connect(cliCore_, &ClientCore::sigMsgAnswer, &wi, &WaitOper::recvFrame); - wo.start(); - checking.exec(); + wi.start(); + checking.exec(); + + if (!infoMsg.list.isEmpty()) { + for (const auto& item : infoMsg.infos.keys()) { + resultFiles << infoMsg.infos[item]; + } + } + + } else { + // 本地自己等待。 + WaitOperOwn wo(this); + wo.func_ = [this, &datas, &resultFiles]() { + resultFiles.clear(); + for (int i = 0; i < (datas.size() / 5); ++i) { + FileStruct fst; + fst.root = GlobalData::Ins()->GetLocalRoot(); + + const auto& curType = datas[i * 5 + 3]->text(); + if (curType == "File") { + fst.mid = datas[i * 5 + 1]->text(); + resultFiles.push_back(fst); + } else { + QVector fst; + DirFileHelper::GetAllFiles(GlobalData::Ins()->GetLocalRoot(), datas[i * 5 + 1]->text(), fst); + if (!fst.isEmpty()) { + resultFiles << fst; + } + } + } + return true; + }; + + LoadingDialog checking(this); + checking.setTipsText("正在获取文件列表..."); + checking.setCanCancel(false); + connect(&wo, &WaitOperOwn::sigOver, &checking, &LoadingDialog::cancelBtnClicked); + + wo.start(); + checking.exec(); + } QVector tasks; - for (int i = 0; i < (datas.size() / 5); ++i) { - if (datas[i * 5 + 3]->text() != "File") { - qDebug() << QString(tr("暂不支持传输文件夹:%1")).arg(datas[i * 5 + 3]->text()); - continue; - } - /* - 要注意这一块的逻辑,本软件的所讲的【上传】【下载】都是针对本地。 - 这里的任务拼接和 DropEvent 有所不同, - DropEvent 是接收方负责拼接任务,但是这里是发送方拼接任务。 - 所以这里的拼接逻辑需要注意。 - */ + // 这里的 file 是相对路径,不再是特定的文件名称。 + for (const auto& file : std::as_const(resultFiles)) { TransTask task; task.taskUUID = Util::UUID(); task.isUpload = !isRemote_; task.localId = cliCore_->GetOwnID(); task.remoteId = cliCore_->GetRemoteID(); if (isRemote_) { - task.remotePath = Util::Join(GlobalData::Ins()->GetRemoteRoot(), datas[i * 5 + 1]->text()); - task.localPath = GlobalData::Ins()->GetLocalRoot(); + task.remotePath = Util::Join(file.root, file.mid, file.relative); + auto filePath = Util::Join(GlobalData::Ins()->GetLocalRoot(), file.mid, file.relative); + task.localPath = Util::GetFileDir(filePath); } else { - task.remotePath = GlobalData::Ins()->GetRemoteRoot(); - task.localPath = Util::Join(GlobalData::Ins()->GetLocalRoot(), datas[i * 5 + 1]->text()); + auto filePath = Util::Join(GlobalData::Ins()->GetRemoteRoot(), file.mid, file.relative); + task.remotePath = Util::GetFileDir(filePath); + task.localPath = Util::Join(file.root, file.mid, file.relative); } - tasks.push_back(task); + tasks.append(task); } if (tasks.isEmpty()) { return; @@ -633,7 +676,7 @@ void FileManager::OperNewFolder() } // 检查结果 - auto msg = oper.GetMsg(); + auto msg = oper.GetMsgConst(); if (msg.msg == STR_NONE || !msg.msg.isEmpty()) { QMessageBox::information(this, tr("提示"), QString(tr("新建失败=>%1")).arg(msg.msg)); } else { @@ -643,7 +686,8 @@ void FileManager::OperNewFolder() nf.type = Dir; nf.fullPath = folder; nf.name = text; - nf.lastModified = QDateTime::currentDateTime().toMSecsSinceEpoch(); + // nf.lastModified = QDateTime::currentDateTime().toMSecsSinceEpoch(); + nf.lastModified = QDateTime::currentMSecsSinceEpoch(); ui->tableWidget->insertRow(0); ShowFileItem(nf, 0); } @@ -700,7 +744,7 @@ void FileManager::OperDelete() } // 检查结果 - auto msg = oper.GetMsg(); + auto msg = oper.GetMsgConst(); if (msg.msg == STR_NONE || !msg.msg.isEmpty()) { QMessageBox::information(this, tr("提示"), QString(tr("删除失败=>%1")).arg(msg.msg)); } else { @@ -787,7 +831,7 @@ void FileManager::OperRename() } // 检查结果 - auto msg = oper.GetMsg(); + auto msg = oper.GetMsgConst(); if (msg.msg == STR_NONE || !msg.msg.isEmpty()) { QMessageBox::information(this, tr("提示"), QString(tr("重命名失败=>%1")).arg(msg.msg)); } else { diff --git a/Protocol/Protocol.h b/Protocol/Protocol.h index 5f20e18..873e23c 100644 --- a/Protocol/Protocol.h +++ b/Protocol/Protocol.h @@ -109,6 +109,7 @@ enum FileCheckState { #define STRMSG_ST_FILENOEXIT "fileNotExist" #define STRMSG_ST_DIREXIT "dirExist" #define STRMSG_ST_DIRNOEXIT "dirNotExist" +#define STRMSG_ST_COMMAND_ALREADY_RUNNING "commandAlreadyRunning" #define STR_FILE "File" #define STR_DIR "Dir" diff --git a/Struct/InfoMsg.h b/Struct/InfoMsg.h index 1be431b..cff95d3 100644 --- a/Struct/InfoMsg.h +++ b/Struct/InfoMsg.h @@ -8,6 +8,12 @@ #include #include +struct FileStruct { + QString root; + QString mid; + QString relative; +}; + struct PropertyData { QString uuid; QString command; @@ -27,21 +33,40 @@ struct InfoMsg { QString type; quint64 size{}; quint32 permissions{}; + QVector listSend; QVector list; QMap mapData; + FileStruct fst; + QMap> infos; void serialize(QDataStream& data) const { data << mark << command << msg << fromPath << toPath << type << size << permissions; + + data << static_cast(listSend.size()); + for (const auto& item : listSend) { + data << item; + } + data << static_cast(list.size()); for (const auto& item : list) { data << item; } + data << static_cast(mapData.size()); for (auto it = mapData.constBegin(); it != mapData.constEnd(); ++it) { data << it.key(); - data << it.value().uuid << it.value().command << it.value().userAction - << it.value().localPath << it.value().remotePath << it.value().state << it.value().properE; + data << it.value().uuid << it.value().command << it.value().userAction << it.value().localPath + << it.value().remotePath << it.value().state << it.value().properE; + } + data << fst.root << fst.mid << fst.relative; + data << static_cast(infos.size()); + for (auto it = infos.constBegin(); it != infos.constEnd(); ++it) { + data << it.key(); + data << static_cast(it.value().size()); + for (const auto& fileStruct : it.value()) { + data << fileStruct.root << fileStruct.mid << fileStruct.relative; + } } } @@ -50,6 +75,12 @@ struct InfoMsg { data >> mark >> command >> msg >> fromPath >> toPath >> type >> size >> permissions; qint32 listSize; + data >> listSize; + listSend.resize(listSize); + for (auto& item : listSend) { + data >> item; + } + data >> listSize; list.resize(listSize); for (auto& item : list) { @@ -63,10 +94,28 @@ struct InfoMsg { QString key; PropertyData prop; data >> key; - data >> prop.uuid >> prop.command >> prop.userAction >> prop.localPath - >> prop.remotePath >> prop.state >> prop.properE; + data >> prop.uuid >> prop.command >> prop.userAction >> prop.localPath >> prop.remotePath >> prop.state >> + prop.properE; mapData.insert(key, prop); } + + data >> fst.root >> fst.mid >> fst.relative; + + data >> mapSize; + infos.clear(); + for (int i = 0; i < mapSize; ++i) { + QString key; + data >> key; + + qint32 vectorSize; + data >> vectorSize; + QVector fileVector; + fileVector.resize(vectorSize); + for (int j = 0; j < vectorSize; ++j) { + data >> fileVector[j].root >> fileVector[j].mid >> fileVector[j].relative; + } + infos.insert(key, fileVector); + } } }; QDataStream& operator<<(QDataStream& data, const InfoMsg& info); diff --git a/Util/Util.cpp b/Util/Util.cpp index 4057363..5874d83 100644 --- a/Util/Util.cpp +++ b/Util/Util.cpp @@ -71,6 +71,11 @@ QString Util::Join(const QString& path, const QString& name) return QDir::cleanPath(path + QDir::separator() + name); } +QString Util::Join(const QString& path, const QString& mid, const QString& name) +{ + return QDir::cleanPath(path + QDir::separator() + mid + QDir::separator() + name); +} + void Util::InitLogger(const QString& logPath, const QString& mark) { auto file_sink = std::make_shared(logPath.toStdString(), 1024 * 1024 * 50, 3); @@ -83,6 +88,12 @@ void Util::InitLogger(const QString& logPath, const QString& mark) spdlog::register_logger(logger); } +QString Util::GetFileDir(const QString& path) +{ + QFileInfo fileInfo(path); + return fileInfo.absolutePath(); +} + // do not check exit QString Util::Get2FilePath(const QString& file, const QString& directory) { @@ -178,7 +189,7 @@ QVector Util::GetLocalDrivers() { QVector result; auto drivers = QStorageInfo::mountedVolumes(); - for (const auto& driver : drivers) { + for (const auto& driver : std::as_const(drivers)) { if (driver.isValid() && driver.isReady()) { result.push_back(driver.rootPath()); } @@ -218,6 +229,38 @@ bool DirFileHelper::GetAllFiles(const QString& rootPath, QVector& files return true; } +bool DirFileHelper::GetAllFiles(const QString& rootPath, const QString& mid, QVector& files) +{ + auto fileRoot = Util::Join(rootPath, mid); + QDir rootDir(fileRoot); + + if (!rootDir.exists()) { + qWarning() << "目录不存在:" << fileRoot; + 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); + } + FileStruct fst; + fst.root = rootPath; + fst.mid = mid; + fst.relative = relativePath; + files.append(fst); + } + } + return true; +} + DirFileHelper::DirFileHelper(QObject* parent) : QObject(parent) { } @@ -337,4 +380,4 @@ QString Util::NewDir(const QString& path) } else { return tr("创建目录失败: %1").arg(path); } -} \ No newline at end of file +} diff --git a/Util/Util.h b/Util/Util.h index 2c64850..e884f4e 100644 --- a/Util/Util.h +++ b/Util/Util.h @@ -2,6 +2,7 @@ #define UTIL_H #include +#include #include #include @@ -45,7 +46,9 @@ public: static QString GetUserHome(); static void InitLogger(const QString& logPath, const QString& mark); static QString Get2FilePath(const QString& file, const QString& directory); + static QString GetFileDir(const QString& path); static QString Join(const QString& path, const QString& name); + static QString Join(const QString& path, const QString& mid, const QString& name); static QString GetCurConfigPath(const QString& sub); static void ConsoleMsgHander(QtMsgType type, const QMessageLogContext& context, const QString& msg); static QString GetVersion(); @@ -68,6 +71,7 @@ public: public: QString GetErr() const; static bool GetAllFiles(const QString& rootPath, QVector& files); + static bool GetAllFiles(const QString& rootPath, const QString& mid, QVector& files); signals: void sigHome(const QString& path, const QVector& drivers);