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下載文件


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM