Compare commits

...

3 Commits

Author SHA1 Message Date
7155880ff3 初步完成文件夹传输。 2026-03-25 14:53:24 +08:00
0e397ace7d 添加文件夹传输进度更新。 2026-03-24 22:25:00 +08:00
5843332980 添加打包时image相关插件安装。 2026-03-24 12:22:01 +08:00
15 changed files with 562 additions and 195 deletions

View File

@@ -17,7 +17,7 @@
"args": [] "args": []
}, },
"cmake.configureSettings": { "cmake.configureSettings": {
"CMAKE_PREFIX_PATH": "C:/dev/qt6", "CMAKE_PREFIX_PATH": "C:/local/qt6",
}, },
"cmake.configureArgs": [ "cmake.configureArgs": [
"-Wno-dev", "-Wno-dev",

View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16) 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 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -76,6 +76,11 @@ set(QT_DEP_STYLES
"${CMAKE_PREFIX_PATH}/plugins/styles/qmodernwindowsstyle.dll" "${CMAKE_PREFIX_PATH}/plugins/styles/qmodernwindowsstyle.dll"
"${CMAKE_PREFIX_PATH}/plugins/styles/qwindowsvistastyle.dll" "${CMAKE_PREFIX_PATH}/plugins/styles/qwindowsvistastyle.dll"
) )
set(QT_IMG_DEP
"${CMAKE_PREFIX_PATH}/plugins/imageformats/qgif.dll"
"${CMAKE_PREFIX_PATH}/plugins/imageformats/qico.dll"
"${CMAKE_PREFIX_PATH}/plugins/imageformats/qjpeg.dll"
)
endif() endif()
set(CMAKE_DEBUG_POSTFIX "d") set(CMAKE_DEBUG_POSTFIX "d")
@@ -109,7 +114,7 @@ function(safe_install_files files_list destination)
if(EXISTS ${file}) if(EXISTS ${file})
list(APPEND existing_files ${file}) list(APPEND existing_files ${file})
else() else()
message(STATUS "跳过: ${file}") message(STATUS "Jump: ${file}")
endif() endif()
endforeach() endforeach()
if(existing_files) if(existing_files)
@@ -129,6 +134,7 @@ if (WIN32)
install(FILES ${QT_DEP_FILES} DESTINATION bin) install(FILES ${QT_DEP_FILES} DESTINATION bin)
safe_install_files("${QT_DEP_PLATFORM}" "bin/platforms") safe_install_files("${QT_DEP_PLATFORM}" "bin/platforms")
safe_install_files("${QT_DEP_STYLES}" "bin/styles") safe_install_files("${QT_DEP_STYLES}" "bin/styles")
safe_install_files("${QT_IMG_DEP}" "bin/imageformats")
endif() endif()
execute_process( execute_process(

View File

@@ -12,6 +12,9 @@ void ClientCore::Instance()
socket_ = new QTcpSocket(this); socket_ = new QTcpSocket(this);
connect(socket_, &QTcpSocket::readyRead, this, &ClientCore::onReadyRead); connect(socket_, &QTcpSocket::readyRead, this, &ClientCore::onReadyRead);
connect(socket_, &QTcpSocket::disconnected, this, &ClientCore::onDisconnected); connect(socket_, &QTcpSocket::disconnected, this, &ClientCore::onDisconnected);
clearWaitTimer_ = new QTimer(this);
clearWaitTimer_->setInterval(10000);
connect(clearWaitTimer_, &QTimer::timeout, this, [this]() { clearWaitTask(); });
} }
ClientCore::~ClientCore() ClientCore::~ClientCore()
@@ -142,10 +145,82 @@ void ClientCore::handleAsk(QSharedPointer<FrameBuffer> frame)
} }
return; return;
} }
// 这个请求的处理可能是耗时的,需要开线程处理。
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<InfoMsg>(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); qWarning() << QString(tr("未知询问信息类型:%1")).arg(msg.command);
} }
void ClientCore::clearWaitTask()
{
QMutexLocker locker(&waitTaskMut_);
QList<QString> 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<FrameBuffer> frame) void ClientCore::UseFrame(QSharedPointer<FrameBuffer> frame)
{ {
switch (frame->type) { switch (frame->type) {
@@ -413,4 +488,108 @@ void WaitThread::interrupCheck()
isAlreadyInter_ = true; isAlreadyInter_ = true;
emit sigCheckOver(); emit sigCheckOver();
} }
} }
WaitOper::WaitOper(QObject* parent) : WaitThread(parent)
{
}
void WaitOper::run()
{
isAlreadyInter_ = false;
infoMsg_.msg = STR_NONE;
isRun_ = true;
recvMsg_ = false;
infoMsg_.command = sendStrType_;
infoMsg_.fromPath = stra_;
infoMsg_.toPath = strb_;
infoMsg_.type = type_;
auto f = cli_->GetBuffer<InfoMsg>(infoMsg_, 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::GetMsgConst() const
{
return infoMsg_;
}
InfoMsg& WaitOper::GetMsgRef()
{
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()
{
auto execRet = false;
if (func_) {
execRet = func_();
}
if (!fid.isEmpty()) {
if (!cli_->syncInvoke(cli_, cli_->GetBuffer<InfoMsg>(infoMsg_, FBT_MSGINFO_ANSWER, fid))) {
qCritical() << QString(tr("向%1发送%2请求失败。")).arg(fid).arg(infoMsg_.command);
}
}
emit sigOver();
}
void WaitOperOwn::recvFrame(QSharedPointer<FrameBuffer> frame)
{
qDebug() << "不应该被调用的地方:" << __FUNCTION__;
}

View File

@@ -15,8 +15,14 @@
#include <QReadWriteLock> #include <QReadWriteLock>
#include <QTcpSocket> #include <QTcpSocket>
#include <QThread> #include <QThread>
#include <QTimer>
#include <array> #include <array>
class WaitOperOwn;
struct WaitTask {
QString id;
WaitOperOwn* wo;
};
class ClientCore : public QObject class ClientCore : public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -93,6 +99,7 @@ private:
void onReadyRead(); void onReadyRead();
void onDisconnected(); void onDisconnected();
void handleAsk(QSharedPointer<FrameBuffer> frame); void handleAsk(QSharedPointer<FrameBuffer> frame);
void clearWaitTask();
private: private:
void UseFrame(QSharedPointer<FrameBuffer> frame); void UseFrame(QSharedPointer<FrameBuffer> frame);
@@ -121,9 +128,13 @@ public:
bool connected_{false}; bool connected_{false};
LocalFile localFile_; LocalFile localFile_;
QTimer* clearWaitTimer_{};
QMutex waitTaskMut_;
QMap<QString, WaitTask> waitTask_;
}; };
// Socket Worker Thread // 工作线程。
class SocketWorker : public QThread class SocketWorker : public QThread
{ {
Q_OBJECT Q_OBJECT
@@ -139,7 +150,7 @@ private:
ClientCore* core_{}; ClientCore* core_{};
}; };
// HeatBeat to Server // 心跳包线程。
class HeatBeat : public QThread class HeatBeat : public QThread
{ {
Q_OBJECT Q_OBJECT
@@ -159,6 +170,7 @@ private:
ClientCore* core_{}; ClientCore* core_{};
}; };
// 耗时操作线程基本框架。
class WaitThread : public QThread class WaitThread : public QThread
{ {
Q_OBJECT Q_OBJECT
@@ -183,4 +195,50 @@ protected:
ClientCore* cli_{}; 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 GetMsgConst() const;
InfoMsg& GetMsgRef();
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:
QString fid;
InfoMsg infoMsg_{};
std::function<bool()> func_;
};
#endif // CLIENTCORE_H #endif // CLIENTCORE_H

View File

@@ -398,7 +398,25 @@ void FileTrans::SendFile(const QSharedPointer<DoTransTask>& task)
QMutexLocker locker(&sthMut_); QMutexLocker locker(&sthMut_);
upTasks_[task->task.localId] = sendThread; 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) SendThread::SendThread(ClientCore* clientCore) : cliCore_(clientCore)
@@ -442,7 +460,7 @@ void SendThread::run()
} }
task_->tranSize += frame->data.size(); task_->tranSize += frame->data.size();
// 关键点:这里不调用,无法中途收到别人发的数据。 // 关键点:这里不调用,无法中途收到别人发的数据。
QCoreApplication::processEvents(); // QCoreApplication::processEvents();
} }
qInfo() << QString(tr("结束发送文件:%1")).arg(task_->file.fileName()); qInfo() << QString(tr("结束发送文件:%1")).arg(task_->file.fileName());

View File

@@ -511,32 +511,103 @@ void FileManager::UpDown()
QMessageBox::information(this, tr("提示"), tr("请选择完整的行。")); QMessageBox::information(this, tr("提示"), tr("请选择完整的行。"));
return; return;
} }
QVector<TransTask> tasks; /*
for (int i = 0; i < (datas.size() / 5); ++i) { 要注意这一块的逻辑,本软件的所讲的【上传】【下载】都是针对本地。
if (datas[i * 5 + 3]->text() != "File") { 这里的任务拼接和 DropEvent 有所不同,
qDebug() << QString(tr("暂不支持传输文件夹:%1")).arg(datas[i * 5 + 3]->text()); DropEvent 是接收方负责拼接任务,但是这里是发送方拼接任务。
continue;
}
/*
要注意这一块的逻辑,本软件的所讲的【上传】【下载】都是针对本地。
这里的任务拼接和 DropEvent 有所不同,
DropEvent 是接收方负责拼接任务,但是这里是发送方拼接任务。
所以这里的拼接逻辑需要注意。 所以这里的拼接逻辑需要注意。
*/ */
QVector<FileStruct> 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();
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") {
fst.relative = datas[i * 5 + 1]->text();
resultFiles << fst;
} else {
infoMsg.infos[datas[i * 5 + 1]->text()] = QVector<FileStruct>{};
}
}
LoadingDialog checking(this);
checking.setTipsText("正在等待对方获取文件列表...");
connect(&wi, &WaitOper::sigCheckOver, &checking, &LoadingDialog::cancelBtnClicked);
connect(cliCore_, &ClientCore::sigMsgAnswer, &wi, &WaitOper::recvFrame);
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<FileStruct> 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<TransTask> tasks;
// 这里的 file 是相对路径,不再是特定的文件名称。
for (const auto& file : std::as_const(resultFiles)) {
TransTask task; TransTask task;
task.taskUUID = Util::UUID(); task.taskUUID = Util::UUID();
task.isUpload = !isRemote_; task.isUpload = !isRemote_;
task.localId = cliCore_->GetOwnID(); task.localId = cliCore_->GetOwnID();
task.remoteId = cliCore_->GetRemoteID(); task.remoteId = cliCore_->GetRemoteID();
if (isRemote_) { if (isRemote_) {
task.remotePath = Util::Join(GlobalData::Ins()->GetRemoteRoot(), datas[i * 5 + 1]->text()); task.remotePath = Util::Join(file.root, file.mid, file.relative);
task.localPath = GlobalData::Ins()->GetLocalRoot(); auto filePath = Util::Join(GlobalData::Ins()->GetLocalRoot(), file.mid, file.relative);
task.localPath = Util::GetFileDir(filePath);
} else { } else {
task.remotePath = GlobalData::Ins()->GetRemoteRoot(); auto filePath = Util::Join(GlobalData::Ins()->GetRemoteRoot(), file.mid, file.relative);
task.localPath = Util::Join(GlobalData::Ins()->GetLocalRoot(), datas[i * 5 + 1]->text()); 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()) { if (tasks.isEmpty()) {
return; return;
@@ -569,7 +640,7 @@ void FileManager::OperNewFolder()
if (!isRemote_) { if (!isRemote_) {
QString ret = Util::NewDir(folder); QString ret = Util::NewDir(folder);
if (ret.isEmpty()) { if (ret.isEmpty()) {
//QMessageBox::information(this, tr("提示"), tr("创建%1成功").arg(folder)); // QMessageBox::information(this, tr("提示"), tr("创建%1成功").arg(folder));
DirFileInfo nf; DirFileInfo nf;
nf.size = 0; nf.size = 0;
nf.type = Dir; nf.type = Dir;
@@ -605,17 +676,18 @@ void FileManager::OperNewFolder()
} }
// 检查结果 // 检查结果
auto msg = oper.GetMsg(); auto msg = oper.GetMsgConst();
if (msg.msg == STR_NONE || !msg.msg.isEmpty()) { if (msg.msg == STR_NONE || !msg.msg.isEmpty()) {
QMessageBox::information(this, tr("提示"), QString(tr("新建失败=>%1")).arg(msg.msg)); QMessageBox::information(this, tr("提示"), QString(tr("新建失败=>%1")).arg(msg.msg));
} else { } else {
//QMessageBox::information(this, tr("提示"), QString(tr("新建%1成功。")).arg(folder)); // QMessageBox::information(this, tr("提示"), QString(tr("新建%1成功。")).arg(folder));
DirFileInfo nf; DirFileInfo nf;
nf.size = 0; nf.size = 0;
nf.type = Dir; nf.type = Dir;
nf.fullPath = folder; nf.fullPath = folder;
nf.name = text; nf.name = text;
nf.lastModified = QDateTime::currentDateTime().toMSecsSinceEpoch(); // nf.lastModified = QDateTime::currentDateTime().toMSecsSinceEpoch();
nf.lastModified = QDateTime::currentMSecsSinceEpoch();
ui->tableWidget->insertRow(0); ui->tableWidget->insertRow(0);
ShowFileItem(nf, 0); ShowFileItem(nf, 0);
} }
@@ -642,7 +714,7 @@ void FileManager::OperDelete()
if (!isRemote_) { if (!isRemote_) {
QString ret = Util::Delete(name); QString ret = Util::Delete(name);
if (ret.isEmpty()) { if (ret.isEmpty()) {
//QMessageBox::information(this, tr("提示"), tr("删除成功")); // QMessageBox::information(this, tr("提示"), tr("删除成功"));
int row = datas[0]->row(); int row = datas[0]->row();
ui->tableWidget->removeRow(row); ui->tableWidget->removeRow(row);
} else { } else {
@@ -672,11 +744,11 @@ void FileManager::OperDelete()
} }
// 检查结果 // 检查结果
auto msg = oper.GetMsg(); auto msg = oper.GetMsgConst();
if (msg.msg == STR_NONE || !msg.msg.isEmpty()) { if (msg.msg == STR_NONE || !msg.msg.isEmpty()) {
QMessageBox::information(this, tr("提示"), QString(tr("删除失败=>%1")).arg(msg.msg)); QMessageBox::information(this, tr("提示"), QString(tr("删除失败=>%1")).arg(msg.msg));
} else { } else {
//QMessageBox::information(this, tr("提示"), QString(tr("删除成功。"))); // QMessageBox::information(this, tr("提示"), QString(tr("删除成功。")));
ui->tableWidget->removeRow(datas[0]->row()); ui->tableWidget->removeRow(datas[0]->row());
} }
} }
@@ -759,7 +831,7 @@ void FileManager::OperRename()
} }
// 检查结果 // 检查结果
auto msg = oper.GetMsg(); auto msg = oper.GetMsgConst();
if (msg.msg == STR_NONE || !msg.msg.isEmpty()) { if (msg.msg == STR_NONE || !msg.msg.isEmpty()) {
QMessageBox::information(this, tr("提示"), QString(tr("重命名失败=>%1")).arg(msg.msg)); QMessageBox::information(this, tr("提示"), QString(tr("重命名失败=>%1")).arg(msg.msg));
} else { } else {
@@ -824,81 +896,4 @@ void FileManager::doubleClick(int row, int column)
QDir dir(GetRoot()); QDir dir(GetRoot());
QString np = dir.filePath(item->text()); QString np = dir.filePath(item->text());
fileHelper_->GetDirFile(np); 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, 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 class FileManager : public QWidget
{ {
Q_OBJECT Q_OBJECT

View File

@@ -6,56 +6,57 @@
## 问题清单: ## 问题清单:
| 序号 | 类型 | 内容 | 说明 | 基于版本 | 完成版本 | | 序号 | 类型 | 内容 | 说明 | 基于版本 | 完成版本 |
| :--: | :--: | ------------------------------------------------------------ | :----: | :------: | :------: | | :--: | :--: | ------------------------------------------------------------ | :------------: | :------: | :------: |
| 51 | 问题 | 按照类型传输时,类型重复的问题修正 | | 0.2.5 | 0.2.6 | | 52 | 问题 | 发送大文件时,UI有不流畅现象。 | 完成版本待观察 | 0.2.6 | |
| 50 | 功能 | 添加日志内容清空按钮。 | | 0.2.4 | 0.2.5 | | 51 | 问题 | 按照类型传输时,类型重复的问题修正 | | 0.2.5 | 0.2.6 |
| 49 | 功能 | 对照传输支持按照类型传输。 | | 0.2.4 | 0.2.5 | | 50 | 功能 | 添加日志内容清空按钮。 | | 0.2.4 | 0.2.5 |
| 48 | 优化 | 选择上传下载移动到右键菜单中。 | | 0.2.4 | 0.2.5 | | 49 | 功能 | 对照传输支持按照类型传输。 | | 0.2.4 | 0.2.5 |
| 47 | 优化 | 删除新建文件(夹)成功时不提示。 | | 0.2.4 | 0.2.5 | | 48 | 优化 | 选择上传下载移动到右键菜单中。 | | 0.2.4 | 0.2.5 |
| 46 | 功能 | 需要一个压缩传输功能。 | | 0.2.4 | | | 47 | 优化 | 删除新建文件(夹)成功时不提示。 | | 0.2.4 | 0.2.5 |
| 45 | 问题 | 出现过重连后无法刷新客户端列表问题。 | 偶发 | 0.2.4 | | | 46 | 功能 | 需要一个压缩传输功能。 | | 0.2.4 | |
| 44 | 问题 | 出现过多选下载显示远程文件不存在问题,但是单个下没问题。 | 偶发 | 0.2.4 | | | 45 | 问题 | 出现过重连后无法刷新客户端列表问题。 | 偶发 | 0.2.4 | |
| 43 | 问题 | 资源占用过多问题。 | | 0.2.3 | 0.2.4 | | 44 | 问题 | 出现过多选下载显示远程文件不存在问题,但是单个下没问题。 | 偶发 | 0.2.4 | |
| 42 | 优化 | 访问文件过多时,界面有卡顿情况。 | | 0.2.3 | 0.2.4 | | 43 | 问题 | 资源占用过多问题。 | | 0.2.3 | 0.2.4 |
| 41 | 问题 | 主动中断检查等待后,软件崩溃| | 0.2.3 | 0.2.4 | | 42 | 优化 | 访问文件过多时,界面有卡顿情况| | 0.2.3 | 0.2.4 |
| 40 | 问题 | 上传方完成后,接收端未接收完毕不能退出的问题。 | | 0.2.3 | 0.2.4 | | 41 | 问题 | 主动中断检查等待后,软件崩溃。 | | 0.2.3 | 0.2.4 |
| 39 | 优化 | Console端断联后应当自动退出 | | 0.2.3 | 0.2.4 | | 40 | 问题 | 上传方完成后,接收端未接收完毕不能退出的问题| | 0.2.3 | 0.2.4 |
| 38 | 功能 | PE中使用没有拖拽功能,文件管理器要有下载/上传按钮。 | | 0.2.3 | 0.2.4 | | 39 | 优化 | Console端断联后应当自动退出。 | | 0.2.3 | 0.2.4 |
| 37 | 问题 | 未连接对方客户端时,执行对照传输会崩溃| 待复现 | 0.2.3 | | | 38 | 功能 | PE中使用没有拖拽功能,文件管理器要有下载/上传按钮| | 0.2.3 | 0.2.4 |
| 36 | 功能 | 备份文件功能。 | | 0.2.3 | | | 37 | 问题 | 未连接对方客户端时,执行对照传输会崩溃。 | 待复现 | 0.2.3 | |
| 35 | 功能 | 完善对照功能的界面其他内容。 | | 0.2.3 | | | 36 | 功能 | 备份文件功能。 | | 0.2.3 | |
| 34 | 功能 | 支持删除、重命名、新建文件(夹)。 | | 0.2.3 | 0.2.4 | | 35 | 功能 | 完善对照功能的界面其他内容。 | | 0.2.3 | |
| 33 | 功能 | 弹窗查看文件(夹)属性窗口。 | | 0.2.3 | 0.2.4 | | 34 | 功能 | 支持删除、重命名、新建文件(夹)。 | | 0.2.3 | 0.2.4 |
| 32 | 功能 | 路径选择显示驱动器。 | | 0.2.2 | 0.2.3 | | 33 | 功能 | 弹窗查看文件(夹)属性窗口。 | | 0.2.3 | 0.2.4 |
| 31 | 功能 | Server端buffer大小判断,有过多无效数据则踢出该客户端。 | | 0.2.2 | 0.2.4 | | 32 | 功能 | 路径选择显示驱动器。 | | 0.2.2 | 0.2.3 |
| 30 | 功能 | 传输界面的取消退出,缺失功能处理。 | | 0.2.2 | 0.2.3 | | 31 | 功能 | Server端buffer大小判断,有过多无效数据则踢出该客户端。 | | 0.2.2 | 0.2.4 |
| 29 | 功能 | 根据接收端的速率限制发送端的速度。 | | 0.2.2 | 0.2.3 | | 30 | 功能 | 传输界面的取消退出,缺失功能处理。 | | 0.2.2 | 0.2.3 |
| 28 | 变更 | 语言简体中文。 | | 0.2.2 | 0.2.3 | | 29 | 功能 | 根据接收端的速率限制发送端的速度。 | | 0.2.2 | 0.2.3 |
| 27 | 功能 | 传输前检查对方或者自己是否已存在某些文件,提示是否覆盖。 | | 0.2.2 | 0.2.3 | | 28 | 变更 | 语言简体中文。 | | 0.2.2 | 0.2.3 |
| 26 | 功能 | 最好能保存关闭界面时Splitter和UI的尺寸。 | | 0.2.2 | | | 27 | 功能 | 传输前检查对方或者自己是否已存在某些文件,提示是否覆盖| | 0.2.2 | 0.2.3 |
| 25 | 功能 | 手动输入的文件夹访问路径保存历史记录,以便后续可能再次使用(10条)。 | | 0.2.2 | | | 26 | 功能 | 最好能保存关闭界面时Splitter和UI的尺寸。 | | 0.2.2 | |
| 24 | 问题 | 发送端如果发送的数据比较大,UI会卡住(功能是正常的)。 | | 0.2.2 | 0.2.3 | | 25 | 功能 | 手动输入的文件夹访问路径保存历史记录,以便后续可能再次使用(10条)。 | | 0.2.2 | |
| 23 | 问题 | 作为接收端如果对方断联要处理这个情况。 | | 0.2.2 | 0.2.3 | | 24 | 问题 | 发送端如果发送的数据比较大,UI会卡住(功能是正常的)。 | | 0.2.2 | 0.2.3 |
| 22 | 功能 | 配置可以切组。 | | 0.2.2 | | | 23 | 问题 | 作为接收端如果对方断联要处理这个情况。 | | 0.2.2 | 0.2.3 |
| 21 | 功能 | 可以传输文件夹。 | | 0.2.2 | | | 22 | 功能 | 配置可以切组。 | | 0.2.2 | |
| 20 | 功能 | 对照传输可以打开本地文件夹。 | | 0.2.2 | 0.2.3 | | 21 | 功能 | 可以传输文件夹。 | | 0.2.2 | |
| 19 | 问题 | 客户之间心跳包,被动/主动失联后断开,清除相关内容,终止相关任务。 | | 0.2.2 | 0.2.3 | | 20 | 功能 | 对照传输可以打开本地文件夹。 | | 0.2.2 | 0.2.3 |
| 18 | 问题 | 断开后重连貌似没有发送心跳包。 | | 0.1 | 0.2 | | 19 | 问题 | 客户之间心跳包,被动/主动失联后断开,清除相关内容,终止相关任务。 | | 0.2.2 | 0.2.3 |
| 17 | 问题 | 服务端主动踢出的客户端,主动重连假链接,不可用无反应。 | | 0.1 | 0.2 | | 18 | 问题 | 断开后重连貌似没有发送心跳包。 | | 0.1 | 0.2 |
| 16 | 优化 | 传输完成后接收端要刷新一次。 | | 0.1 | | | 17 | 问题 | 服务端主动踢出的客户端,主动重连假链接,不可用无反应。 | | 0.1 | 0.2 |
| 15 | 问题 | 拖动文件夹到对方,应当不处理,并日志提示。 | | 0.1 | 0.2 | | 16 | 优化 | 传输完成后接收端要刷新一次。 | | 0.1 | |
| 14 | 功能 | 文件浏览部分添加右键复制全路径功能。 | | 0.1 | 0.2 | | 15 | 问题 | 拖动文件夹到对方,应当不处理,并日志提示。 | | 0.1 | 0.2 |
| 13 | 功能 | IP端口部分添加可选择历史| | 0.1 | 0.2 | | 14 | 功能 | 文件浏览部分添加右键复制全路径功能| | 0.1 | 0.2 |
| 12 | 功能 | 对照传输前要先校验文件存在性。 | | 0.1 | 0.2.3 | | 13 | 功能 | IP端口部分要添加可选择历史。 | | 0.1 | 0.2 |
| 11 | 问题 | 断连后当前远端ID要清除。 | | 0.1 | 0.2 | | 12 | 功能 | 对照传输前要先校验文件存在性。 | | 0.1 | 0.2.3 |
| 10 | 功能 | 对照传输可以支持反向下载。 | | 0.1 | 0.2 | | 11 | 问题 | 断连后当前远端ID要清除。 | | 0.1 | 0.2 |
| 9 | 优化 | 输入路径后可以直接回车访问。 | | 0.1 | 0.2 | | 10 | 功能 | 对照传输可以支持反向下载。 | | 0.1 | 0.2 |
| 8 | 功能 | 文件浏览页面要支持按照类型排序,时间排序,和文件后缀筛选。 | | 0.1 | 0.2 | | 9 | 优化 | 输入路径后可以直接回车访问。 | | 0.1 | 0.2 |
| 7 | 优化 | IP和Port宽度要合理一些,IP过小,Port宽度过大 | | 0.1 | 0.2 | | 8 | 功能 | 文件浏览页面要支持按照类型排序,时间排序,和文件后缀筛选| | 0.1 | 0.2 |
| 6 | 功能 | 比对控件添加可尝试目录浏览控件跳转到指定目录。 | | 0.1 | 0.2 | | 7 | 优化 | IP和Port宽度要合理一些,IP过小,Port宽度过大。 | | 0.1 | 0.2 |
| 5 | 问题 | 未设置心跳包,导致超时被Server端踢出。 | 必改 | 0.1 | 0.2 | | 6 | 功能 | 比对控件添加可尝试目录浏览控件跳转到指定目录。 | | 0.1 | 0.2 |
| 4 | 问题 | 重复链接Server会崩溃。 | | 0.1 | 0.2 | | 5 | 问题 | 未设置心跳包,导致超时被Server端踢出。 | 必改 | 0.1 | 0.2 |
| 3 | 优化 | 首次启动时应当按照当前分辨率进行初始化大小,而不是指定大小。 | | 0.1 | 0.2 | | 4 | 问题 | 重复链接Server会崩溃。 | | 0.1 | 0.2 |
| 2 | 问题 | 当某个文件传输失败的时候,关闭传输窗口,再次传输显示已经在传输中。 | | 0.1 | 0.2.3 | | 3 | 优化 | 首次启动时应当按照当前分辨率进行初始化大小,而不是指定大小。 | | 0.1 | 0.2 |
| 1 | 问题 | Console端显示未注册QSharedPointer\<FrameBuffer\>类型 | | 0.1 | 0.2 | | 2 | 问题 | 当某个文件传输失败的时候,关闭传输窗口,再次传输显示已经在传输中。 | | 0.1 | 0.2.3 |
| 1 | 问题 | Console端显示未注册QSharedPointer\<FrameBuffer\>类型 | | 0.1 | 0.2 |

View File

@@ -102,10 +102,14 @@ enum FileCheckState {
#define STRMSG_AC_UP "upAction" #define STRMSG_AC_UP "upAction"
#define STRMSG_AC_DOWN "downAction" #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_FILEEXIT "fileExist"
#define STRMSG_ST_FILENOEXIT "fileNotExist" #define STRMSG_ST_FILENOEXIT "fileNotExist"
#define STRMSG_ST_DIREXIT "dirExist" #define STRMSG_ST_DIREXIT "dirExist"
#define STRMSG_ST_DIRNOEXIT "dirNotExist" #define STRMSG_ST_DIRNOEXIT "dirNotExist"
#define STRMSG_ST_COMMAND_ALREADY_RUNNING "commandAlreadyRunning"
#define STR_FILE "File" #define STR_FILE "File"
#define STR_DIR "Dir" #define STR_DIR "Dir"

View File

@@ -1,6 +1,6 @@
@echo on @echo on
set QT_DIR=C:/Qt/6.8.3/mingw_64 set QT_DIR=C:/local/qt6
cd .. cd ..
cmake -G "MinGW Makefiles" -Bbuild-qt6 -S. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=%QT_DIR% -DQT_DEFAULT_MAJOR_VERSION=6 -DCOMPILE_GUI=ON -DRELEASE_MARK=ON cmake -G "MinGW Makefiles" -Bbuild-qt6 -S. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=%QT_DIR% -DQT_DEFAULT_MAJOR_VERSION=6 -DCOMPILE_GUI=ON -DRELEASE_MARK=ON
cmake --build build-qt6 --config Release cmake --build build-qt6 --config Release

View File

@@ -8,6 +8,12 @@
#include <QString> #include <QString>
#include <QVector> #include <QVector>
struct FileStruct {
QString root;
QString mid;
QString relative;
};
struct PropertyData { struct PropertyData {
QString uuid; QString uuid;
QString command; QString command;
@@ -27,21 +33,40 @@ struct InfoMsg {
QString type; QString type;
quint64 size{}; quint64 size{};
quint32 permissions{}; quint32 permissions{};
QVector<QString> listSend;
QVector<QString> list; QVector<QString> list;
QMap<QString, PropertyData> mapData; QMap<QString, PropertyData> mapData;
FileStruct fst;
QMap<QString, QVector<FileStruct>> infos;
void serialize(QDataStream& data) const void serialize(QDataStream& data) const
{ {
data << mark << command << msg << fromPath << toPath << type << size << permissions; data << mark << command << msg << fromPath << toPath << type << size << permissions;
data << static_cast<qint32>(listSend.size());
for (const auto& item : listSend) {
data << item;
}
data << static_cast<qint32>(list.size()); data << static_cast<qint32>(list.size());
for (const auto& item : list) { for (const auto& item : list) {
data << item; data << item;
} }
data << static_cast<qint32>(mapData.size()); data << static_cast<qint32>(mapData.size());
for (auto it = mapData.constBegin(); it != mapData.constEnd(); ++it) { for (auto it = mapData.constBegin(); it != mapData.constEnd(); ++it) {
data << it.key(); data << it.key();
data << it.value().uuid << it.value().command << it.value().userAction data << it.value().uuid << it.value().command << it.value().userAction << it.value().localPath
<< it.value().localPath << it.value().remotePath << it.value().state << it.value().properE; << it.value().remotePath << it.value().state << it.value().properE;
}
data << fst.root << fst.mid << fst.relative;
data << static_cast<qint32>(infos.size());
for (auto it = infos.constBegin(); it != infos.constEnd(); ++it) {
data << it.key();
data << static_cast<qint32>(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; data >> mark >> command >> msg >> fromPath >> toPath >> type >> size >> permissions;
qint32 listSize; qint32 listSize;
data >> listSize;
listSend.resize(listSize);
for (auto& item : listSend) {
data >> item;
}
data >> listSize; data >> listSize;
list.resize(listSize); list.resize(listSize);
for (auto& item : list) { for (auto& item : list) {
@@ -63,10 +94,28 @@ struct InfoMsg {
QString key; QString key;
PropertyData prop; PropertyData prop;
data >> key; data >> key;
data >> prop.uuid >> prop.command >> prop.userAction >> prop.localPath data >> prop.uuid >> prop.command >> prop.userAction >> prop.localPath >> prop.remotePath >> prop.state >>
>> prop.remotePath >> prop.state >> prop.properE; prop.properE;
mapData.insert(key, prop); 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<FileStruct> 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); QDataStream& operator<<(QDataStream& data, const InfoMsg& info);

View File

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

View File

@@ -21,4 +21,4 @@ add_executable(frelayTest ${MSOURCES})
target_link_libraries(frelayTest PRIVATE Protocol Util) target_link_libraries(frelayTest PRIVATE Protocol Util)
add_executable(frelayBaseTest BaseTest.cpp) 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 <QDateTime>
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
#include <QDirIterator>
#include <QFileDevice> #include <QFileDevice>
#include <QFileInfo> #include <QFileInfo>
#include <QMutex> #include <QMutex>
@@ -70,6 +71,11 @@ QString Util::Join(const QString& path, const QString& name)
return QDir::cleanPath(path + QDir::separator() + 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) void Util::InitLogger(const QString& logPath, const QString& mark)
{ {
auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(logPath.toStdString(), 1024 * 1024 * 50, 3); auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(logPath.toStdString(), 1024 * 1024 * 50, 3);
@@ -82,6 +88,12 @@ void Util::InitLogger(const QString& logPath, const QString& mark)
spdlog::register_logger(logger); spdlog::register_logger(logger);
} }
QString Util::GetFileDir(const QString& path)
{
QFileInfo fileInfo(path);
return fileInfo.absolutePath();
}
// do not check exit // do not check exit
QString Util::Get2FilePath(const QString& file, const QString& directory) QString Util::Get2FilePath(const QString& file, const QString& directory)
{ {
@@ -177,7 +189,7 @@ QVector<QString> Util::GetLocalDrivers()
{ {
QVector<QString> result; QVector<QString> result;
auto drivers = QStorageInfo::mountedVolumes(); auto drivers = QStorageInfo::mountedVolumes();
for (const auto& driver : drivers) { for (const auto& driver : std::as_const(drivers)) {
if (driver.isValid() && driver.isReady()) { if (driver.isValid() && driver.isReady()) {
result.push_back(driver.rootPath()); result.push_back(driver.rootPath());
} }
@@ -190,6 +202,65 @@ QString DirFileHelper::GetErr() const
return QString(); 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;
}
bool DirFileHelper::GetAllFiles(const QString& rootPath, const QString& mid, QVector<FileStruct>& 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) DirFileHelper::DirFileHelper(QObject* parent) : QObject(parent)
{ {
} }
@@ -309,4 +380,4 @@ QString Util::NewDir(const QString& path)
} else { } else {
return tr("创建目录失败: %1").arg(path); return tr("创建目录失败: %1").arg(path);
} }
} }

View File

@@ -2,6 +2,7 @@
#define UTIL_H #define UTIL_H
#include <InfoDirFile.h> #include <InfoDirFile.h>
#include <InfoMsg.h>
#include <QMutex> #include <QMutex>
#include <QObject> #include <QObject>
@@ -45,7 +46,9 @@ public:
static QString GetUserHome(); static QString GetUserHome();
static void InitLogger(const QString& logPath, const QString& mark); static void InitLogger(const QString& logPath, const QString& mark);
static QString Get2FilePath(const QString& file, const QString& directory); 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& name);
static QString Join(const QString& path, const QString& mid, const QString& name);
static QString GetCurConfigPath(const QString& sub); static QString GetCurConfigPath(const QString& sub);
static void ConsoleMsgHander(QtMsgType type, const QMessageLogContext& context, const QString& msg); static void ConsoleMsgHander(QtMsgType type, const QMessageLogContext& context, const QString& msg);
static QString GetVersion(); static QString GetVersion();
@@ -67,6 +70,8 @@ public:
public: public:
QString GetErr() const; QString GetErr() const;
static bool GetAllFiles(const QString& rootPath, QVector<QString>& files);
static bool GetAllFiles(const QString& rootPath, const QString& mid, QVector<FileStruct>& files);
signals: signals:
void sigHome(const QString& path, const QVector<QString>& drivers); void sigHome(const QString& path, const QVector<QString>& drivers);