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