分类: 嵌入式
2013-07-24 17:38:02
在Qt中,提供了QNetworkAccessManager这个类,用于完成基于Http协议的数据上传和下载,该类既可以发送网络请求,也可以接受网络回复。而具体的网络请求是通过QNetworkRequest类发送的,具体的网络回复是通过QNetworkReply类来接收的。
本文将利用上面提到的几个类实现使用Http协议,获取指定的页面,并说明如何向该页面传递POST参数,最后在此基础上添加一个进度条,用于检测页面文件读取进度。
由于QNetworkAccessManager类中包含了一组标准的数据请求函数,因此可以通过该类的对象发送数据请求函数;每个请求函数执行完毕时都回返回一个QNetworkReply对象。当所有请求的数据都到达本地后,将引发一个finished()信号,该信号关联了一个处理返回数据的槽函数。具体的实现可参考下述代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//新建QNetworkAccessManager对象
manager = new QNetworkAccessManager(this);
//关联信号和槽
connect(manager,SIGNAL(finished(QNetworkReply*)),
this,SLOT(replyFinished(QNetworkReply*)));
//发送请求
}
|
由于上面的这些类属于网络模块,所以需要在工程文件.pro中添加下面的语句,表明我们使用了网络模块:
QT += network
另外,还需要在有文件中添加包含头文件:
1
|
#include
|
可以看到,上述的基本原理大部分都在构造函数中完成。首先创建了一个QNetworkAccessManager对象manager;接着将manager所引发的finished()信号与replyFinished()槽进行关联;最后通过get()发送数据请求。
get()用于发送请求并获得目标地址中的数据,具体的数据请求则是通过创建一个QNetworkRequest类的对象而完成的。只要数据请求发送成功,则开始下载数据。当所有的数据下载完成后,就返回一个QNetworkReply类型的对象。同时manager对象将发送一个finished()信号,引发replyFinished槽函数的执行。
当执行上述的槽函数时,就说明目标地址的数据已经下载完毕。此时槽函数要做的就是将这些数据显示出来。这里我们只对文本数据进行转换。对这些数据的转换动作可参考下述的代码:
1
2
3
4
5
6
7
8
9
10
|
void Widget::replyFinished(QNetworkReply *reply)
{
//使用utf8编码,这样才可以显示中文
QTextCodec *codec = QTextCodec::codecForName("utf8");
QString all = codec->toUnicode(reply->readAll());
ui->textBrowser->setText(all);
reply->deleteLater(); //最后要释放reply对象
}
|
为了能够正确显示中文,我们创建QTextCodec对象。利用readAll函数可以读取数据请求返回的所有数据,并且利用toUnicode函数将这些数据转换成QString类型。最后在用户界面中的TextBrower控件中显示出来。
按照上面的方法就可以下载指定地址的数据。如下图:
当返回的数据显示完毕后,利用deleteLater函数将返回的数据删除。
上面的获取指定页面的方式是采用get的方式获取的,如果想通过post的方式传递参数,需要自己构造HTTP请求头部,在上面的代码中,将原有的构造函数代码修改成下面的代码即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//参数
QByteArray content = "name=user&pwd=123";
int contentLength = content.length();
//构造请求
QNetworkRequest req;
req.setHeader(QNetworkRequest::ContentTypeHeader,
"application/x-www-form-urlencoded");
req.setHeader(QNetworkRequest::ContentLengthHeader,contentLength);
//新建QNetworkAccessManager对象
manager = new QNetworkAccessManager(this);
//关联信号和槽
connect(manager,SIGNAL(finished(QNetworkReply*)),
this,SLOT(replyFinished(QNetworkReply*)));
//以Post方式请求页面
manager->post(req,content);
}
|
在上面的程序中,以post的方式请求指定页面,如果获取成功了,则会调用replyFinished这个槽函数,该函数的处理过程还是和原先的一样。
现在,我们使用上面提到的这些类下载网上文件,一般我们下载文件都想要看到下载进度,所以现在,我们需要给请求文件的过程添加进度条,显示当前下载的进度。
现在在界面上,需要放置一个ProcessBar控件,用于显示获取页面或文件下载的进度,还要有一个LineEdit控件,用于获取页面URL,页面布局如下:
widget.h头文件中代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
void startRequest(QUrl url); //请求链接
~Widget();
private:
Ui::Widget *ui;
QNetworkAccessManager *manager;
QNetworkReply *reply;
QUrl url; //存储网络地址
private slots:
//下载按钮的单击事件槽函数
void on_pushButton_clicked();
//完成下载后的处理
void httpFinished();
//接收到数据时的处理
void httpReadyRead();
//更新进度条
void updateDataReadProgress(qint64,qint64);
};
|
然后在构造函数中初始化manager变量,以及隐藏进度条。
1
2
|
manager = new QNetworkAccessManager(this);
ui->progressBar->hide();
|
当点击按钮时,开始加载指定的页面,单击按钮信号的处理槽函数如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
void Widget::on_pushButton_clicked()
{
url = ui->lineEdit->text();
QFileInfo info(url.path());
QString fileName(info.fileName());
//获取文件名
if (fileName.isEmpty())
{
//如果文件名为空,则使用“index.html”,
fileName = "index.html";
}
file = new QFile(fileName);
if(!file->open(QIODevice::WriteOnly))
{
//如果打开文件失败,则删除file,并使file指针为0,然后返回
qDebug() << "file open error";
delete file;
file = 0;
return;
}
//进行链接请求
startRequest(url);
//进度条的值设为0
ui->progressBar->setValue(0);
//显示进度条
ui->progressBar->show();
}
|
这里先从界面中获取输入的地址,然后分解出文件名。因为地址中可能没有文件名(比如输入),这时我们就使用一个默认的文件名。然后我们用这个文件名新建一个文件,然后我们以写入方式打开文件。最后进行链接,并显示进度条。
链接请求函数startRequest代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
void Widget::startRequest(QUrl url)
{
reply = manager->get(QNetworkRequest(url));
//下面关联信号和槽
//下载完成后
connect(reply,SIGNAL(finished()),this,SLOT(httpFinished()));
//有可用数据时
connect(reply,SIGNAL(readyRead()),this,SLOT(httpReadyRead()));
//更新进度条
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),
this,SLOT(updateDataReadProgress(qint64,qint64)));
}
|
上面程序中的manager->get(QNetworkRequest(url))语句返回的是一个QNetworkReply对象,这里我们获得这个对象,使用它完成显示数据下载进度的功能。这里主要是关联了几个信号和槽。当有可用数据时,reply就会发出readyRead()信号,我们这时就可以将可用的数据保存下来。就是在这里,实现了数据分段下载保存,这样下载完所有数据再保存,要节省很多内存。而利用reply的downloadProgress()信号,很容易就实现了进度条的显示。
当有可用数据时,reply就会发出readyRead()信号,我们这时就可以将可用的数据保存下来,相应的槽函数如下:
1
2
3
4
5
6
7
8
|
void Widget::httpReadyRead()
{
if (file)
{
//如果文件存在,则写入文件
file->write(reply->readAll());
}
}
|
这里当file可用时,将下载的数据写入文件。
每当有数据到来时,都更新进度条。
1
2
3
4
5
|
void Widget::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes)
{
ui->progressBar->setMaximum(totalBytes); //最大值
ui->progressBar->setValue(bytesRead); //当前值
}
|
1
2
3
4
5
6
7
8
9
10
|
void Widget::httpFinished() //完成下载
{
ui->progressBar->hide();
file->flush();
file->close();
reply->deleteLater();
reply = 0;
delete file;
file = 0;
}
|
这里只是当下载完成后,进行一些处理。
最后的程序运行结果如下所示: