Qt与HTTP服务器通讯

http://foolog.net/?p=2157

 

 

在Qt中,提供了QNetworkAccessManager这个类,用于完成基于Http协议的数据上传和下载,该类既可以发送网络请求,也可以接受网络回复。而具体的网络请求是通过QNetworkRequest类发送的,具体的网络回复是通过QNetworkReply类来接收的。

本文将利用上面提到的几个类实现使用Http协议,获取指定的页面,并说明如何向该页面传递POST参数,最后在此基础上添加一个进度条,用于检测页面文件读取进度。

基本原理

由于QNetworkAccessManager类中包含了一组标准的数据请求函数,因此可以通过该类的对象发送数据请求函数;每个请求函数执行完毕时都回返回一个QNetworkReply对象。当所有请求的数据都到达本地后,将引发一个finished()信号,该信号关联了一个处理返回数据的槽函数。具体的实现可参考下述代码:

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*)));
 //发送请求
 manager->get(QNetworkRequest(QUrl("http://wap.foolog.sinaapp.com")));
}

 

由于上面的这些类属于网络模块,所以需要在工程文件.pro中添加下面的语句,表明我们使用了网络模块:

QT += network

另外,还需要在有文件中添加包含头文件:

1
#include <QtNetwork>

可以看到,上述的基本原理大部分都在构造函数中完成。首先创建了一个QNetworkAccessManager对象manager;接着将manager所引发的finished()信号与replyFinished()槽进行关联;最后通过get()发送数据请求。

get()用于发送请求并获得目标地址中的数据,具体的数据请求则是通过创建一个QNetworkRequest类的对象而完成的。只要数据请求发送成功,则开始下载数据。当所有的数据下载完成后,就返回一个QNetworkReply类型的对象。同时manager对象将发送一个finished()信号,引发replyFinished槽函数的执行。

当执行上述的槽函数时,就说明目标地址的数据已经下载完毕。此时槽函数要做的就是将这些数据显示出来。这里我们只对文本数据进行转换。对这些数据的转换动作可参考下述的代码:

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控件中显示出来。

按照上面的方法就可以下载指定地址的数据。如下图:

Qt使用Http协议获取指定页面内容

当返回的数据显示完毕后,利用deleteLater函数将返回的数据删除。

发送Post请求

上面的获取指定页面的方式是采用get的方式获取的,如果想通过post的方式传递参数,需要自己构造HTTP请求头部,在上面的代码中,将原有的构造函数代码修改成下面的代码即可。

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.setUrl(QUrl("http://localhost/login.php"));
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,页面布局如下:

Qt下载页面文件:页面布局

类的头文件

widget.h头文件中代码:

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变量,以及隐藏进度条。

manager = new QNetworkAccessManager(this);
ui->progressBar->hide();

 

单击下载按钮处理函数

当点击按钮时,开始加载指定的页面,单击按钮信号的处理槽函数如下:

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();
}

 

这里先从界面中获取输入的地址,然后分解出文件名。因为地址中可能没有文件名(比如输入www.foolog.sinaapp.com),这时我们就使用一个默认的文件名。然后我们用这个文件名新建一个文件,然后我们以写入方式打开文件。最后进行链接,并显示进度条。

链接请求函数

链接请求函数startRequest代码如下:

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()信号,我们这时就可以将可用的数据保存下来,相应的槽函数如下:

void Widget::httpReadyRead()
{
    if (file)
    {
        //如果文件存在,则写入文件
        file->write(reply->readAll());
    }
}

 

这里当file可用时,将下载的数据写入文件。

更新进度条函数

每当有数据到来时,都更新进度条。

void Widget::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes)
{
    ui->progressBar->setMaximum(totalBytes); //最大值
    ui->progressBar->setValue(bytesRead);  //当前值
}

 

完成下载

void Widget::httpFinished()  //完成下载
{
    ui->progressBar->hide();
    file->flush();
    file->close();
    reply->deleteLater();
    reply = 0;
    delete file;
    file = 0;
}

 

这里只是当下载完成后,进行一些处理。

最后的程序运行结果如下所示:

Qt使用HTTP下载文件

posted on 2015-06-18 11:55  pTrack  阅读(4951)  评论(0编辑  收藏  举报