基于Qt的Http编程-进阶

上文的Http客户端只能下载指定网址的数据,这样的客户端在交互性和功能性上都很差。本文所描述的程序则在这个基本的客户端上进行改造,实现任意目标地址的数据下载,并且改善了用户界面的。具体UI可参考下图:

基于Qt的Http编程-进阶_第1张图片

对于UI而言,该客户端增加了任意地址的输入框、下载进度条和下载按钮;对于下载的数据而言,该客户端不再局限于下载文本数据。接下来本文将按照程序执行的大致顺序对相关函数进行分析。

构造函数

用户界面的设计可见本文参考1和参考2,这里从构造函数开始。在构造函数中除了对用户界面进行初始化外,还创建了一个QNetworkAccessManager对象,并将进度显示条隐藏了起来。

1 widget::widget(QWidget *parent) :
2     QWidget(parent),
3     ui(new Ui::widget)
4 {
5     ui->setupUi(this);
6     manager = new QNetworkAccessManager(this);
7     ui->progressBar->hide();
8 }

on_pushButton_clicked槽函数

该槽函数对应着下载按钮的单击事件。该函数完成的工作有从用户界面获得下载地址;从这个地址中解析出所下载文件的名称;打开(创建)所下载的文件;发送下载请求;更新进度显示条的信息。

从上文中可以看到,在Qt中使用QUrl类来存储url地址,通过该类的成员函数还可以根据具体情况对url地址进行相应的处理。使用QFileInfo类的对象存储不依赖具体系统的文件属性,比如文件的名称,路径,访问权限等;通过该类的成员函数可以方便的获取文件的某些属性,比如通过fileName成员函数就可以从路径名中快速解析出文件名。

得到了文件名就可以创建一个QFile类的对象,通过open成员函数就可以打开这个文件,QIODevice::WriteOnly为打开模式。如果打开错误,则弹出警告提示框,并进行相应的错误处理。

文件打开成功后,紧接着就应该发送下载链接的请求了,这个工作将在startRequest()中完成。最后设置进度更新条的初始值,并将其在界面上显示出来。具体的实现代码可参考下图:

01 void widget::on_pushButton_clicked()
02 {
03     url.setUrl(ui->lineEdit->text());
04     QFileInfo info(url.path());//获得地址;
05     QString fileName(info.fileName());//从地址中获得文件名;
06  
07     //如果地址类似www.edsionte.com,则文件名为index.html;
08     if (fileName.isEmpty())
09         fileName = "index.html";
10  
11     file = new QFile(fileName);
12     if (!file->open(QIODevice::WriteOnly))
13     {
14         QMessageBox::warning(this, tr("Warning"),
15                              tr("file open error"),
16                              QMessageBox::Yes);
17         qDebug() << "file open error";
18         delete file;
19         file = 0;
20         return;
21    }
22    startRequest(url);//进行请求;
23    ui->progressBar->setValue(0);
24    ui->progressBar->show();
25 }

startRequest()的实现

通过参考下面的示例代码就可以发现,该函数注意进行了两部分的内容:发送下载请求并获得数据回复和几组信号和槽函数之间的连接。

get()函数在上文中已有说明,它将返回一个QNetworkReply类的对象reply。当所有的数据下载完毕后,manager对象将发送finished信号,进而调用httpFinished槽函数。

上文所描述的Http客户端是等待所有的数据都下载到内存再读出并显示,而本文所描述的Http客户端则将请求的数据进行分段下载并保存。因此每当有一部分新数据到达本地,reply就会发送readyRead信号,进而调用httpReadyRead槽函数将这部分新的数据保存到本地。使用这种分段下载并保存数据的方法可以有效的节省内存。而一旦网络请求有数据返回,reply对象就会发送downloadProgress信号,进而引发updataReadProcess槽函数对进度显示条进行更新。

01 void widget::startRequest(QUrl url)
02 {
03     reply = manager->get(QNetworkRequest(url));
04  
05     connect(reply, SIGNAL(finished()),
06             this, SLOT(httpFinished()));
07  
08     connect(reply, SIGNAL(readyRead()),
09             this, SLOT(httpReadyRead()));
10  
11     connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
12             this, SLOT(updataReadProcess(qint64,qint64)));
13 }

相关槽函数

当有一部分新数据返回到本地后,就回执行httpReadyRead函数。该函数将这部分新到达内存的数据写入file对象所对应的文件中。

1 void widget::httpReadyRead()
2 {
3     //如果文件存在,则将数据写入文件;
4     if (file)
5         file->write(reply->readAll());
6 }

每当请求的数据有返回时,就执行updataReadProcess函数以便及时更新进度显示条。该函数包含两个参数byteRead和totalBytes。前者表示当前接受到的数据总量,后者表示应该下载的数据总量。显然,随着byteRead的增加,进度显示条也不断更新。当byteRead和totalBytes相等时,表示下载完毕。

1 void widget::updataReadProcess(qint64 byteRead, qint64 totalBytes)
2 {
3     ui->progressBar->setMaximum(totalBytes);//最大值;
4     ui->progressBar->setValue(byteRead);//当前值
5 }

当所有数据都下载完毕后,就执行httpFinished函数。在该函数中将隐藏进度显示条,并将缓冲区的数据清空。并且关闭文件对象,再释放之前申请的一些数据空间。

01 void widget::httpFinished()
02 {
03     ui->progressBar->hide();
04     file->flush();
05     file->close();
06     reply->deleteLater();
07     reply = 0;
08     delete file;
09     file = 0;
10 }

至此,基本上完成了一个Http下载客户端。理论上可以下载任何数据,不过我在测试中发现有些地址不能如期下载(比如QQ的下载链接)。

你可能感兴趣的:(http协议,QT4)