Qt案例 以单线程或者单生产者多消费者设计模式实现QFTP模块上传文件夹功能
前文:Qt案例 使用QFtpServerLib开源库实现Qt软件搭建FTP服务器,使用QFTP模块访问FTP服务器
已经介绍了Qt环境下搭建FTP服务器或者使用QFTP上传的方式示例,
这里主要介绍下使用QFTP模块上传整个文件夹的案例示例。
目录导读
- 前因后果
- 单线程处理
- 1.定义FTPFolderUpload 继承 QThread线程类
- 2.等待FTP中的命令执行完毕
- 3.遍历本地文件夹上传文件夹
- 生产者消费者模式
- 1. 定义一个UploadItme 结构用于数据交互,
- 2. 定义生产者方法,
- 3. 定义消费者
- 4. 定义缓存区
- 5. 开始线程调用
前因后果
一开始听到使用QFTP模块上传文件夹这个需求的时候,我还觉得简单,没啥难度,无非是遍历文件夹,上传文件罢了,能有多大问题,
于是我在界面上创建文件再上传文件,正常上传没问题,
但是当我遍历文件目录,按照手动创建文件夹的流程一条条写入mkdir(),cd(),put() 命令时,软件直接崩了。
不知道是由于执行的命令太多,还是上传的文件QFile* 太多了造成的,也没有用QBreakpad捕捉崩溃,
既然按照手动操作的方式上传是没有问题,那么
事急从权
所以为了五一假期能耍舒服点,手动正常上传那么我就参照手动上传的模式,
等上一个命令执行完了,在执行下一条命令,
给FTP客户端一个反应时间,以避免文件夹还没创建就开始上传文件了。
于是通过qftp.h
头文件找到 QFtp::hasPendingCommands()
函数判断上一条命令是否执行完毕。
既: 通过循环休眠判断当前FTP客户端中的命令是否执行完毕。
// QFtp* FtpClient;qint64 count=1;while(FtpClient->hasPendingCommands()){// qDebug()<<count<<".hasPendingCommands--> "<<QDateTime::currentDateTime()<<""<<FtpClient->state();//! 等待所有命令完成 这里的数值调小有助于提速,但是过小那么循环的意义不大msleep(100);count++;//! 如果链接时间超过30秒则异常if(count>s && s!=-1)return false;}return true;
但是这样一来就有个问题,如果在主界面这么做,会直接把QFTP的事件堵塞,也就是一直卡着,不会执行命令,一开始没注意,多次测试堵塞了FTP客户端的事件执行,导致连FTP服务器都没连上。
于是我创建了一个继承QThread线程的类,在这个线程类内监控QFtp类的命令项是否执行完毕,执行完毕就继续遍历本地文件夹,在FTP服务器创建文件夹和上传文件。
单线程处理
在新的QThread类中处理的时候,唯一要注意的就是FTP客户端所在线程不能一致,
所以需要先在主界面创建一个QFTP*类变量(FTP客户端),再传递给QThread线程类处理。
#include <QObject>
#include <QWidget>
#include <QThread>
#include "qftp.h"//! 等待ftp命令完成
#define WAITFTPOVER AwaitingExecution()
#define WAITFTPOVERS(_S_) AwaitingExecution(_S_)//! 继承QThread线程
class FTPFolderUpload:public QThread
{Q_OBJECT
public:QThread_FolderUpload(QObject *parent=nullptr);~QThread_FolderUpload();//! 初始化参数void InitData(QFtp* FtpClient,QString ftproot,QString dirpath);void run();//! 返回异常信息QString GetError(){return ErrorStr;};//! 等待当前FTP命令执行完毕//! 避免在没有创建完文件夹前就开始上传文件bool AwaitingExecution(int s=30);//! 遍历文件目录void TraverseTheFolder(QString FtpDir,QString Dirpath);
private://! Ftp服务器的相对位置QString FtpRoot;//! 需要上传的文件夹QString DirPath;//! Ftp客户端QFtp* FtpClient=nullptr;//! 出现异常信息QString ErrorStr;
};
等待FTP中的命令执行完毕就是循环判断是否还有未处理的的命令,
如果有则QThread::msleep
休眠等待执行完毕
bool FTPFolderUpload::AwaitingExecution(int s)
{if(FtpClient==nullptr)return false;qint64 count=1;while(FtpClient->hasPendingCommands()){//! 等待所有命令完成 这里的数值调小有助于提速,msleep(100);count++;//! 如果链接时间超过30秒则异常 -设置一个默认时间if(count>s && s!=-1)return false;}return true;
}
遍历本地文件夹,同时上传文件,每次上传文件或者创建文件夹时都需要等待前面所有的命令执行完毕,
否则上传的文件很可能上传到其他文件夹或者服务器根目录中
void FTPFolderUpload::TraverseTheFolder(QString FtpDir,QString dirpath)
{ FtpClient->cd