1 前言
前面讲解了主流的TCP/UDP请求后,该到用处最多的HTTP请求了,在Qt中要使用HTTP请求,在Qt4的时候有专门的QHttp,QFtp类使用,可惜在Qt5已经全部干掉了,统一封装了接口QNetworkAccessManager ,下面主要就是围绕该类展开一个系统性的介绍和使用!玩转此类,Qt5的网络请求基本就不在话下了!
公众号:Qt实战,各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发。
公众号:Qt入门和进阶,专门介绍Qt/C++相关知识点学习,帮助Qt开发者更好的深入学习Qt。多位Qt元婴期大神,一步步带你从入门到进阶,走上财务自由之路。
官方店:https://shop114595942.taobao.com//
这里提前放一个动态示意图,可以让读者再开始之前知道是怎么一回事
2 Qt高层网络操作类介绍
Qt网络模块提供–些类实现OSI7层网络模型中高层的网络协议,如HTTP、FTP、SNMP等,这些类主要是QNetworkRequest、QNetworkReply 和QNetworkAccessManager。
QNetworkRequest类通过一个URL地址发起网络协议请求,也保存网络请求的信息,目前支持HTTP、FTP和局部文件URL的下载或上传。
QNetworkAccessManager 类用于协调网络操作。在QNetworkRequest发起一个网络请求后,QNetworkAccessManager类负责发送网络请求,创建网络响应。
QNetworkReply 类表示网络请求的响应。由QNetworkAccessManager在发送一个 网络请求后创建-一个网络响应。QNetworkReply 提供的信号finished()、 readyRead() 和downloadProgress() 可以监测网络响应的执行情况,执行相应操作。
QNetworkReply是QIODevice的子类,所以QNetworkReply支持流读写功能,也支持异步或同步工作模式。
3 基于HTTP协议的网络文件下载(包含下载进度、当前下载速度、剩余时间)
基于上述三个类,设计一个基于HTTP协议的网络文件下载程序,实例程序名称HTTP_Demo, 下图是程序运行下载文件时的界面。
在URL地址编辑框里输入一个网络文件URL地址,设置下载文件保存路径后,单击下载按钮就可以开始下载文件到设置的目录下。进度条可以显示文件下载进度,当前下载速度可以实时显示当前网速状态,剩余时间显示完成的时间度。URL里的HTTP地址可以是任何类型的文件,如html、pdf、 doc、exe等。
实例HTTP_Demo主界面是基于QWidget的窗口类HTTP_Demo,使用UI设计器设计界面,删除了主窗口上的工具栏和状态栏。HTTP_Demo类的定义如下:
QT_BEGIN_NAMESPACE
namespace Ui { class HTTP_Demo; }
QT_END_NAMESPACE
class HTTP_Demo : public QWidget
{
Q_OBJECT
public:
HTTP_Demo(QWidget *parent = nullptr);
~HTTP_Demo();
//时间格式化输出
QString timeFormat(qint64 seconds);
//速度格式化输出
QString speed(double speed);
//已经下载的大小格式化输出
QString size(qint64 bytes);
//计算下载进度各种数据,实时下载速度,已下载大小,剩余时间等
void calculateDownloadProgress(int timeDifference);
private slots:
//使用缺省值默认路径触发
void on_pushButton_defaultPath_clicked();
//选择文件夹路径触发
void on_pushButton_selectFilePath_clicked();
//下载按钮触发
void on_pushButton_download_clicked();
//下载完成后的槽
void onFinish();
void onReadyRead();
void onDownloadProgress(qint64,qint64);
private:
Ui::HTTP_Demo *ui;
QNetworkAccessManager mNetworkManager; //网络管理
QNetworkReply *mPReply = nullptr; //网络响应
QFile * mPdownloadedFile = nullptr;//下载保存的临时文件
QTime m_timeRecord; //记录下载实时网速用时
int m_timeInterval= 0; //时间记录的间隔差距
qint64 m_bytesReceived = 0; ///当前的接收数据大小
qint64 m_bytesTotal = 0; ///当前下载的数据总大小
qint64 m_currentDownloadSize = 0; ///当前已经下载的大小
qint64 m_intervalDownloadSize = 0; ///计算速度之前下载的大小的累计和,计算速度用
};
#endif // HTTP_DEMO_H
要下载文件,先在窗口上的URL编辑框里输入下载地址(可以使用Ctrl+V组合键粘贴URL地址),再设置下载文件保存的目录。单击缺省路径按钮会在程序的当前目录下创建一个临时文件夹,代码如下:
void HTTP_Demo::on_pushButton_defaultPath_clicked()
{
//缺省路径按钮
QString curPath="C:/Users/Administrator/Downloads";
QDir dir (curPath) ;
QString sub="temp";
dir.mkdir (sub) ;
ui->lineEdit_fileSavePath->setText (curPath+"/"+sub+"/") ;
}
单击选择文件路径按钮会在打开一个文件夹路径选择框,在选择的文件夹路径下创建一个临时文件夹,代码如下:
void HTTP_Demo::on_pushButton_selectFilePath_clicked()
{
QString selectdir = QFileDialog::getExistingDirectory(this,
tr("Open Directory"),
"/home",
QFileDialog::ShowDirsOnly|
QFileDialog::DontResolveSymlinks);
QDir dir (selectdir) ;
QString sub="temp";
dir.mkdir (sub) ;
ui->lineEdit_fileSavePath->setText(selectdir+"/"+sub+"/");
}
选择的效果动态图如下:
选择自己的保存路径后,然后填写下载URL,单击下载 按钮开始下载过程,下载 按钮的响应代码如下:
void HTTP_Demo::on_pushButton_download_clicked()
{
//开始下载
QString urlSpec = ui->lineEdit_downloadURL->text ().trimmed() ;
if (urlSpec. isEmpty() )
{
QMessageBox::information(this, "错误","请指定需要下载的URL") ;
return;
}
bool isHttpsRequest;
//不区分大小写判断是否是https请求,
if(urlSpec.startsWith("https:"), Qt::CaseInsensitive);
{
isHttpsRequest = true;
}
QUrl newUrl = QUrl(urlSpec) ;
if ( !newUrl. isValid() )
{
QMessageBox:: information(this,"错误",QString("无效URL: 81 \n错误信息: 82") .arg (urlSpec, newUrl.errorString())) ;
return;
}
QString tempDir =ui->lineEdit_fileSavePath->text().trimmed() ;
if (tempDir. isEmpty())
{
QMessageBox::information(this, "错误", "请指定保存下载文件的目录") ;
return;
}
QString fileName = newUrl.fileName ();
//如果找不到文件名称,就默认写一个
if(fileName.isEmpty())
{
fileName = "temp.date";
}
QString fullFileName =tempDir+fileName ;
if (QFile::exists (fullFileName))
{
QFile::remove(fullFileName) ;
}
mPdownloadedFile =new QFile (fullFileName) ;
if (!mPdownloadedFile->open(QIODevice::Append))
{
QMessageBox::information(this, "错误" , "临时文件打开错误") ;
return;
}
ui->pushButton_download->setEnabled(false) ;
if(mNetworkManager == nullptr)
{
mNetworkManager = new QNetworkAccessManager(this);
}
QNetworkRequest request2(newUrl);
//*********
if(isHttpsRequest)
{
//设置SSL,HTTPS协议需要SSL证书
QSslConfiguration m_sslConfig = QSslConfiguration::defaultConfiguration();
m_sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
m_sslConfig.setProtocol(QSsl::TlsV1SslV3);
request2.setSslConfiguration(m_sslConfig);
}
request2.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
request2.setRawHeader("Connection","keep alive");
//******这一段有没有其实都无所谓,我是后面尝试测试过的,主要是ssl库有就行了
mPReply = mNetworkManager->get(request2) ;
connect (mPReply, &QNetworkReply::finished, this, &HTTP_Demo::onFinish) ;
connect (mPReply, &QNetworkReply::readyRead, this, &HTTP_Demo::onReadyRead) ;
connect (mPReply, &QNetworkReply::downloadProgress, this, &HTTP_Demo::onDownloadProgress) ;
m_timeRecord.start();
}
代码在读取URL地址后,将其转换为一个QUrl类变量newUrl,并检查其有效性,再检查临时文件目录,创建临时文件downloadedFile。
这些准备好之后,用QNetworkAccessManager发布网络请求,请求下载URL地址表示的文件,并创建网络响应,关键代码为:
mPReply = mNetworkManager.get(QNetworkRequest(newUrl)) ;
mPReply为网络响应,将其3个信号与相关的自定义槽函数相关联,实现相应的操作。这3个槽函数的代码如下:
void HTTP_Demo::onFinish()
{
//网络响应结束
QFileInfo fileInfo;
fileInfo.setFile(mPdownloadedFile->fileName() ) ;
mPdownloadedFile->close() ;
delete mPdownloadedFile;
mPdownloadedFile = nullptr;
mPReply->deleteLater() ;
mPReply = nullptr;
ui->pushButton_download->setEnabled(true) ;
}
void HTTP_Demo::onReadyRead()
{
//实取下载的数据
mPdownloadedFile->write(mPReply->readAll() ) ;
}
void HTTP_Demo::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
m_bytesReceived = bytesReceived;
m_bytesTotal = bytesTotal;
//下载进程,设置最大值为100
ui->progressBar->setMaximum(100) ;
calculateDownloadProgress(300);
}
void HTTP_Demo::calculateDownloadProgress(int timeDifference)
{
//计算您当前的进度
float currentProgressValue = static_cast<float>(m_bytesReceived)/static_cast<float>(m_bytesTotal)*100;
ui->progressBar-> setValue(currentProgressValue) ;
// 输出当前下载进度;
// 用到除法需要注意除0错误;
//qDebug() << QString("%1").arg(bytesReceived * 100 / bytesTotal + 1);
// m_intervalDownload 为下次计算速度之前的下载字节数;
m_intervalDownloadSize += m_bytesReceived - m_currentDownloadSize;
m_currentDownloadSize = m_bytesReceived;
int timeNow = m_timeRecord.elapsed();
// 超过0.3s更新计算一次速度;
if (timeNow - m_timeInterval > timeDifference)
{
qint64 ispeed = m_intervalDownloadSize * 1000 / (timeNow - m_timeInterval);
QString strSpeed = speed(ispeed);
ui->label_speed->setText("当前下载速度:"+strSpeed);
// 剩余时间;
qint64 timeRemain = 0;
if (ispeed != 0)
{
timeRemain = (m_bytesTotal - m_bytesReceived) / ispeed;
}
//转换单位(下载剩余时间)
QString strTimeRemain = timeFormat(timeRemain);
ui->label_time->setText("剩余时间:"+strTimeRemain);
//当前已下载的数据大小
QString currentFileDownSize = size(m_currentDownloadSize);
ui->label_downSize->setText("已下载大小:"+currentFileDownSize);
//下载文件的总大小
QString totalFileSize = size(m_bytesTotal);
ui->label_totalSize->setText("文件总大小:"+totalFileSize);
m_intervalDownloadSize = 0;
m_timeInterval = timeNow;
}
}
在缓冲区有新下载的数据等待读取时,会发射readyRead()信号,槽函数 onReadyRead()读取下载缓冲区的数据到临时文件。
downloadProgress()是表示网络操作进度的信号,传递bytesReceived和bytesTotal两个参数,表示已读取字节数和总的字节数; onDownloadProgress()槽 函数将这两个参数用于进度条的显示,可以显示下载进度。
finished() 信号在下载结束后发射,槽函数onFinish()的功能是关闭临时文件,删除文件变量和网络响应变量。
4 下载动态示意图
例如,如果下载的是一个Qt5.12.10文件,不知道为何使用Qt官方的下载链接会报错,下载总大小会出错,后面改为了清华大学的才正常,下面贴出下载链接https://mirrors.tuna.tsinghua.edu.cn/qt/archive/qt/5.12/5.12.10/qt-opensource-windows-x86-5.12.10.exe
因为是https请求,所以请求需要SSL库,Mingw编译的请在自己的Qt安装库中找到对应的libssl和libcrypto库,下面是我的路径:
D:Qt\Qt5.12.10\Tools\QtCreator\bin\libssl-1_1-x64.dll或者是libssl-1_1.dll
D:Qt\Qt5.12.10\Tools\QtCreator\bin\libcrypto-1_1-x64.dll或者是libcrypto-1_1.dll
MSVC编译的请自行编译,Qt好像是不带MSVC的,如果找不到,可以查看我这边文章
Windows10+VS2017下安装和 编译openssl 1.1版本库
请记得将两个dll库放到与exe的位置一起
好了,下面是我的https下载的动态效果图如下:
因为下载拉取数据有卡顿,所以到12%的时候进度卡主了很久,这个是和服务器有关系,然后我测试了这个链接http://vfx.mtime.cn/Video/2019/03/21/mp4/190321153853126488.mp4
下面是下载动态图效果:
下载完成后
播放是没问题的,所以下载是没问题的
为了演示下载大文件的性能,去除服务端问题,就像上面Qt下载总是有问题,这种就说明不了是Qt网络库的问题,所以我找到了一个百M的链接,下载360软件,84M 链接如下:
https://dl.360safe.com/pclianmeng/n/1__4000943.exe?source=%E6%9C%A8%E9%A9%AC%E6%9F%A5%E6%9D%80
通过360官网拿到的,实在找不到其他更大的链接,电影链接都是磁力的,目前也不支持,我们只说HTTP/HTTPS下载的
下载完成后如下图:
所以从这方便看,基本上下载是没多大问题的,好了有需要的可以在自己再深入了解吧!