目录
- 概述
- 实践理论
- QNetworkAccessManager进行Http编程的基本步骤
- 代码实战(重点片段)
概述
网络访问 API 建立在一个 QNetworkAccessManager
对象之上,该对象保存了发送请求所需的公共配置和设置。它包含代理和缓存配置,以及与此类问题相关的信号和可用于监视网络操作进度的回复信号。整个 Qt 应用程序只需要一个 QNetworkAccessManager 实例。由于 QNetworkAccessManager 基于 QObject,因此只能从它所属的线程中使用。
创建了 QNetworkAccessManager 对象后,应用程序可以使用它来通过网络发送请求。提供了一组标准函数,这些函数接受请求和可选数据,并返回一个 QNetworkReply
对象。返回的对象用于获取响应对应请求返回的任何数据。
一旦你创建了QNetworkAccessManager对象,就可以使用它提供的一组标准方法来发送网络请求,而每个方法都会返回一个QNetworkReply对象,这个对象代表了与你发送的请求相应的反馈,通过它,你既可以读取网络应答的元数据,又可以读取实体数据。QNetworkAccessManager的get()
、post()
、head()
、put()
、deleteResourse()
、sendCustomRequest()
等方法可用于发送网络请求,http
、ftp
、https
都是小菜一碟,只要你构造一个QNetworkRequest对象传递给它们就行了。
如果你仔细研究QNetworkAccessManager的接口及其枚举类型Operation
,还有QNetworkRequest的接口及其KnownHeaders
、Attribute
等枚举类型,可能会发现,其实,它好像就是专门为http编程设计的,毕竟,http协议在网络世界里广泛使用。不过喜欢ftp的朋友也别灰心,QNetworkAccessManager是万能的,它也支持下载、上传等常见的ftp操作。
实践理论
我们使用QNetworkAccessManager进行网络编程,与4个类打交道比较多一些,这里先介绍一下,为后面的代码解释做做准备。
QNetworkAccessManager
是中心,它提供了发送请求的各种API以及网络配置相关的方法。QNetworkRequest
代表一个网络请求,包括URL、HTTP访问用的UserAgent、用户名、密码等都通过它设置。QNetworkReply
是QNetworkAccessManager根据你提交的QNetworkRequest构造的一个应答对象,一旦请求发出,你就可以通过它获知网络操作过程中的各种状态。QUrl
是构造请求(QNetworkRequest)时常用的类,它可以编解码URL,提取URL中的主机、路径、文件、用户名、密码等信息。
QNetworkAccessManager进行Http编程的基本步骤
- 根据URL生成QUrl对象,然后根据QUrl创建一个网络请求QNetworkRequest,必要时可设置一些header。
- 调用QNetworkAccessManager的get()、post()等方法中的一个。
- 使用QNetworkAccessManager返回的QNetworkReply实例来跟踪应答的各种状态反馈。
如果要上传数据到服务器,则需要提供一个QIODevice
指针或者QByteArray
给post()
方法。通常情况下你可以使用QFile
打开指定的文件然后传给post()方法来上传本地文件。而后者则允许你直接将裸数据上传到服务器。
需要特别说明的是:
- 开发者必须自己选择合适的时机释放QNetworkAccessManager返回的QNetworkReply对象。
- 请不要在响应QNetworkReply对象的信号时直接删除QNetworkReply对象,应该使用其
deleteLater()
方法延迟销毁它。 - 对于重定向消息,可以通过QNetworkReply::header(QNetworkRequest::LocationHeader)获取定向到的新地址,然后针对新地址重走长征路。
代码实战(重点片段)
首先要在qmake或cmake构建中添加相关配置:
Header: #include <QNetworkAccessManager>
CMake: find_package(Qt6 REQUIRED COMPONENTS Network)
target_link_libraries(mytarget PRIVATE Qt6::Network)
qmake: QT += network
QNetworkAccessManager m_nam;
QNetworkReply * m_reply;
QNetworkRequest * m_request;
QString m_strFilePath;
QFile * m_file;
*************************************************
Widget::Widget(QWidget* parent): QWidget(parent), m_nam(this), m_reply(0), m_request(0), m_file(0) {
setupGUI();
}
Widget::~Widget() {
if (m_file) {
m_file->close();
delete m_file;
}
if (m_reply) {
m_reply->disconnect(this);
m_reply->abort();
delete m_reply;
}
}
static const char* g_ext_to_content_type[] = {
".001", "application/x-001"
, ".301", "application/x-301"
, ".323", "text/h323"
, ".906", "application/x-906"
, ".907", "drawing/907"
, ".a11", "application/x-a11"
, ".acp", "audio/x-mei-aac"
, ".ai", "application/postscript"
, ".aif", "audio/aiff"
, ".aifc", "audio/aiff"
, ".aiff", "audio/aiff"
, ".anv", "application/x-anv"
, ".asa", "text/asa"
, ".asf", "video/x-ms-asf"
, ".asp", "text/asp"
, ".asx", "video/x-ms-asf"
, ".au", "audio/basic"
, ".avi", "video/avi"
, ".awf", "application/vnd.adobe.workflow"
, ".biz", "text/xml"
, ".bmp", "application/x-bmp"
, ".bot", "application/x-bot"
, ".c4t", "application/x-c4t"
, ".c90", "application/x-c90"
, ".cal", "application/x-cals"
, ".cat", "application/vnd.ms-pki.seccat"
, ".cdf", "application/x-netcdf"
, ".cdr", "application/x-cdr"
, ".cel", "application/x-cel"
, ".cer", "application/x-x509-ca-cert"
, ".cg4", "application/x-g4"
, ".cgm", "application/x-cgm"
, ".cit", "application/x-cit"
, ".class", "java/*"
, ".cml", "text/xml"
, ".cmp", "application/x-cmp"
, ".cmx", "application/x-cmx"
, ".cot", "application/x-cot"
, ".conf", "text/plain"
, ".crl", "application/pkix-crl"
, ".crt", "application/x-x509-ca-cert"
, ".csi", "application/x-csi"
, ".css", "text/css"
, ".cut", "application/x-cut"
, ".dbf", "application/x-dbf"
, ".dbm", "application/x-dbm"
, ".dbx", "application/x-dbx"
, ".dcd", "text/xml"
, ".dcx", "application/x-dcx"
, ".der", "application/x-x509-ca-cert"
, ".dgn", "application/x-dgn"
, ".dib", "application/x-dib"
, ".dll", "application/x-msdownload"
, ".doc", "application/msword"
, ".dot", "application/msword"
, ".drw", "application/x-drw"
, ".dtd", "text/xml"
, ".dwf", "Model/vnd.dwf"
, ".dwf", "application/x-dwf"
, ".dwg", "application/x-dwg"
, ".dxb", "application/x-dxb"
, ".dxf", "application/x-dxf"
, ".edn", "application/vnd.adobe.edn"
, ".emf", "application/x-emf"
, ".eml", "message/rfc822"
, ".ent", "text/xml"
, ".epi", "application/x-epi"
, ".eps", "application/x-ps"
, ".eps", "application/postscript"
, ".etd", "application/x-ebx"
, ".exe", "application/x-msdownload"
, ".fax", "image/fax"
, ".fdf", "application/vnd.fdf"
, ".fif", "application/fractals"
, ".fo", "text/xml"
, ".frm", "application/x-frm"
, ".g4", "application/x-g4"
, ".gbr", "application/x-gbr"
, ".gcd", "application/x-gcd"
, ".gif", "image/gif"
, ".gl2", "application/x-gl2"
, ".gp4", "application/x-gp4"
, ".hgl", "application/x-hgl"
, ".hmr", "application/x-hmr"
, ".hpg", "application/x-hpgl"
, ".hpl", "application/x-hpl"
, ".hqx", "application/mac-binhex40"
, ".hrf", "application/x-hrf"
, ".hta", "application/hta"
, ".htc", "text/x-component"
, ".htm", "text/html"
, ".html", "text/html"
, ".htt", "text/webviewhtml"
, ".htx", "text/html"
, ".icb", "application/x-icb"
, ".ico", "image/x-icon"
, ".ico", "application/x-ico"
, ".iff", "application/x-iff"
, ".ig4", "application/x-g4"
, ".igs", "application/x-igs"
, ".iii", "application/x-iphone"
, ".img", "application/x-img"
, ".ini", "text/plain"
, ".ins", "application/x-internet-signup"
, ".isp", "application/x-internet-signup"
, ".IVF", "video/x-ivf"
, ".java", "java/*"
, ".jfif", "image/jpeg"
, ".jpe", "image/jpeg"
, ".jpe", "application/x-jpe"
, ".jpeg", "image/jpeg"
, ".jpg", "image/jpeg"
, ".jpg", "application/x-jpg"
, ".js", "application/x-javascript"
, ".jsp", "text/html"
, ".la1", "audio/x-liquid-file"
, ".lar", "application/x-laplayer-reg"
, ".latex", "application/x-latex"
, ".lavs", "audio/x-liquid-secure"
, ".lbm", "application/x-lbm"
, ".lmsff", "audio/x-la-lms"
, ".log", "text/plain"
, ".ls", "application/x-javascript"
, ".ltr", "application/x-ltr"
, ".m1v", "video/x-mpeg"
, ".m2v", "video/x-mpeg"
, ".m3u", "audio/mpegurl"
, ".m4e", "video/mpeg4"
, ".mac", "application/x-mac"
, ".man", "application/x-troff-man"
, ".math", "text/xml"
, ".mdb", "application/msaccess"
, ".mdb", "application/x-mdb"
, ".mfp", "application/x-shockwave-flash"
, ".mht", "message/rfc822"
, ".mhtml", "message/rfc822"
, ".mi", "application/x-mi"
, ".mid", "audio/mid"
, ".midi", "audio/mid"
, ".mil", "application/x-mil"
, ".mml", "text/xml"
, ".mnd", "audio/x-musicnet-download"
, ".mns", "audio/x-musicnet-stream"
, ".mocha", "application/x-javascript"
, ".movie", "video/x-sgi-movie"
, ".mp1", "audio/mp1"
, ".mp2", "audio/mp2"
, ".mp2v", "video/mpeg"
, ".mp3", "audio/mp3"
, ".mp4", "video/mpeg4"
, ".mpa", "video/x-mpg"
, ".mpd", "application/vnd.ms-project"
, ".mpe", "video/x-mpeg"
, ".mpeg", "video/mpg"
, ".mpg", "video/mpg"
, ".mpga", "audio/rn-mpeg"
, ".mpp", "application/vnd.ms-project"
, ".mps", "video/x-mpeg"
, ".mpt", "application/vnd.ms-project"
, ".mpv", "video/mpg"
, ".mpv2", "video/mpeg"
, ".mpw", "application/vnd.ms-project"
, ".mpx", "application/vnd.ms-project"
, ".mtx", "text/xml"
, ".mxp", "application/x-mmxp"
, ".net", "image/pnetvue"
, ".nrf", "application/x-nrf"
, ".nws", "message/rfc822"
, ".odc", "text/x-ms-odc"
, ".out", "application/x-out"
, ".p10", "application/pkcs10"
, ".p12", "application/x-pkcs12"
, ".p7b", "application/x-pkcs7-certificates"
, ".p7c", "application/pkcs7-mime"
, ".p7m", "application/pkcs7-mime"
, ".p7r", "application/x-pkcs7-certreqresp"
, ".p7s", "application/pkcs7-signature"
, ".pc5", "application/x-pc5"
, ".pci", "application/x-pci"
, ".pcl", "application/x-pcl"
, ".pcx", "application/x-pcx"
, ".pdf", "application/pdf"
, ".pdf", "application/pdf"
, ".pdx", "application/vnd.adobe.pdx"
, ".pfx", "application/x-pkcs12"
, ".pgl", "application/x-pgl"
, ".pic", "application/x-pic"
, ".pko", "application/vnd.ms-pki.pko"
, ".pl", "application/x-perl"
, ".plg", "text/html"
, ".pls", "audio/scpls"
, ".plt", "application/x-plt"
, ".png", "image/png"
, ".png", "application/x-png"
, ".pot", "application/vnd.ms-powerpoint"
, ".ppa", "application/vnd.ms-powerpoint"
, ".ppm", "application/x-ppm"
, ".pps", "application/vnd.ms-powerpoint"
, ".ppt", "application/vnd.ms-powerpoint"
, ".ppt", "application/x-ppt"
, ".pr", "application/x-pr"
, ".prf", "application/pics-rules"
, ".prn", "application/x-prn"
, ".prt", "application/x-prt"
, ".ps", "application/x-ps"
, ".ps", "application/postscript"
, ".ptn", "application/x-ptn"
, ".pwz", "application/vnd.ms-powerpoint"
, ".r3t", "text/vnd.rn-realtext3d"
, ".ra", "audio/vnd.rn-realaudio"
, ".ram", "audio/x-pn-realaudio"
, ".ras", "application/x-ras"
, ".rat", "application/rat-file"
, ".rdf", "text/xml"
, ".rec", "application/vnd.rn-recording"
, ".red", "application/x-red"
, ".rgb", "application/x-rgb"
, ".rjs", "application/vnd.rn-realsystem-rjs"
, ".rjt", "application/vnd.rn-realsystem-rjt"
, ".rlc", "application/x-rlc"
, ".rle", "application/x-rle"
, ".rm", "application/vnd.rn-realmedia"
, ".rmf", "application/vnd.adobe.rmf"
, ".rmi", "audio/mid"
, ".rmj", "application/vnd.rn-realsystem-rmj"
, ".rmm", "audio/x-pn-realaudio"
, ".rmp", "application/vnd.rn-rn_music_package"
, ".rms", "application/vnd.rn-realmedia-secure"
, ".rmvb", "application/vnd.rn-realmedia-vbr"
, ".rmx", "application/vnd.rn-realsystem-rmx"
, ".rnx", "application/vnd.rn-realplayer"
, ".rp", "image/vnd.rn-realpix"
, ".rpm", "audio/x-pn-realaudio-plugin"
, ".rsml", "application/vnd.rn-rsml"
, ".rt", "text/vnd.rn-realtext"
, ".rtf", "application/msword"
, ".rtf", "application/x-rtf"
, ".rv", "video/vnd.rn-realvideo"
, ".sam", "application/x-sam"
, ".sat", "application/x-sat"
, ".sdp", "application/sdp"
, ".sdw", "application/x-sdw"
, ".sit", "application/x-stuffit"
, ".slb", "application/x-slb"
, ".sld", "application/x-sld"
, ".slk", "drawing/x-slk"
, ".smi", "application/smil"
, ".smil", "application/smil"
, ".smk", "application/x-smk"
, ".snd", "audio/basic"
, ".sol", "text/plain"
, ".sor", "text/plain"
, ".spc", "application/x-pkcs7-certificates"
, ".spl", "application/futuresplash"
, ".spp", "text/xml"
, ".ssm", "application/streamingmedia"
, ".sst", "application/vnd.ms-pki.certstore"
, ".stl", "application/vnd.ms-pki.stl"
, ".stm", "text/html"
, ".sty", "application/x-sty"
, ".svg", "text/xml"
, ".swf", "application/x-shockwave-flash"
, ".tdf", "application/x-tdf"
, ".tg4", "application/x-tg4"
, ".tga", "application/x-tga"
, ".tif", "image/tiff"
, ".tif", "application/x-tif"
, ".tiff", "image/tiff"
, ".tld", "text/xml"
, ".top", "drawing/x-top"
, ".torrent", "application/x-bittorrent"
, ".tsd", "text/xml"
, ".txt", "text/plain"
, ".uin", "application/x-icq"
, ".uls", "text/iuls"
, ".vcf", "text/x-vcard"
, ".vda", "application/x-vda"
, ".vdx", "application/vnd.visio"
, ".vml", "text/xml"
, ".vpg", "application/x-vpeg005"
, ".vsd", "application/vnd.visio"
, ".vsd", "application/x-vsd"
, ".vss", "application/vnd.visio"
, ".vst", "application/vnd.visio"
, ".vst", "application/x-vst"
, ".vsw", "application/vnd.visio"
, ".vsx", "application/vnd.visio"
, ".vtx", "application/vnd.visio"
, ".vxml", "text/xml"
, ".wav", "audio/wav"
, ".wax", "audio/x-ms-wax"
, ".wb1", "application/x-wb1"
, ".wb2", "application/x-wb2"
, ".wb3", "application/x-wb3"
, ".wbmp", "image/vnd.wap.wbmp"
, ".wiz", "application/msword"
, ".wk3", "application/x-wk3"
, ".wk4", "application/x-wk4"
, ".wkq", "application/x-wkq"
, ".wks", "application/x-wks"
, ".wm", "video/x-ms-wm"
, ".wma", "audio/x-ms-wma"
, ".wmd", "application/x-ms-wmd"
, ".wmf", "application/x-wmf"
, ".wml", "text/vnd.wap.wml"
, ".wmv", "video/x-ms-wmv"
, ".wmx", "video/x-ms-wmx"
, ".wmz", "application/x-ms-wmz"
, ".wp6", "application/x-wp6"
, ".wpd", "application/x-wpd"
, ".wpg", "application/x-wpg"
, ".wpl", "application/vnd.ms-wpl"
, ".wq1", "application/x-wq1"
, ".wr1", "application/x-wr1"
, ".wri", "application/x-wri"
, ".wrk", "application/x-wrk"
, ".ws", "application/x-ws"
, ".ws2", "application/x-ws"
, ".wsc", "text/scriptlet"
, ".wsdl", "text/xml"
, ".wvx", "video/x-ms-wvx"
, ".xdp", "application/vnd.adobe.xdp"
, ".xdr", "text/xml"
, ".xfd", "application/vnd.adobe.xfd"
, ".xfdf", "application/vnd.adobe.xfdf"
, ".xhtml", "text/html"
, ".xls", "application/vnd.ms-excel"
, ".xls", "application/x-xls"
, ".xlw", "application/x-xlw"
, ".xml", "text/xml"
, ".xpl", "audio/scpls"
, ".xq", "text/xml"
, ".xql", "text/xml"
, ".xquery", "text/xml"
, ".xsd", "text/xml"
, ".xsl", "text/xml"
, ".xslt", "text/xml"
, ".xwd", "application/x-xwd"
, ".x_b", "application/x-x_b"
, ".x_t", "application/x-x_t"
};
static const char* get_content_type(char* filename) {
int i = 0;
int size = sizeof(g_ext_to_content_type) / sizeof(g_ext_to_content_type[0]);
char* ext = filename;
char* slash = strrchr(filename, '/');
if (slash)
slash++;
else
slash = filename;
ext = strrchr(filename, '.');
if (!ext)
return "application/octet-stream";
for (; i < size; i += 2) {
if (!strcmp(ext, g_ext_to_content_type[i]))
return g_ext_to_content_type[i + 1];
}
return "application/octet-stream";
}
QString Widget::requestString(QNetworkRequest* req) {
QString joinedHeader;
QVariant variant = req->header(QNetworkRequest::ContentTypeHeader);
if (variant.isValid()) {
joinedHeader += "content-type: ";
joinedHeader += variant.toString();
joinedHeader += "\n";
}
variant = req->header(QNetworkRequest::UserAgentHeader);
if (variant.isValid()) {
joinedHeader += "user-agent: ";
joinedHeader += variant.toString();
joinedHeader += "\n";
}
QList<QByteArray> headerList = req->rawHeaderList();
int count = headerList.size();
QByteArray value;
for (int i = 0; i < count; i++) {
const QByteArray& tag = headerList.at(i);
value = req->rawHeader(tag);
joinedHeader += QString("%1: %2\n").arg(tag.data()).arg(value.data());
}
return joinedHeader;
}
QString Widget::responseString(QNetworkReply* reply) {
QString joinedHeader;
QVariant variant = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (variant.isValid())
joinedHeader = QString("STATUS: %1\n").arg(variant.toInt());
QList<QByteArray> headerList = reply->rawHeaderList();
int count = headerList.size();
QByteArray value;
for (int i = 0; i < count; i++) {
const QByteArray& tag = headerList.at(i);
value = reply->rawHeader(tag);
joinedHeader += QString("%1: %2\n").arg(tag.data()).arg(value.data());
}
return joinedHeader;
}
void Widget::onAddHttpHeaderValue() {
if (!m_request)
m_request = new QNetworkRequest();
QString header = m_headerKeyEdit->text();
if (header.compare("user-agent", Qt::CaseInsensitive) == 0)
m_request->setHeader(QNetworkRequest::UserAgentHeader, m_headerValueEdit->text());
m_request->setRawHeader(header, m_headerValueEdit->text());
m_stateText->setText(requestString(m_request));
}
void Widget::onStart() {
if (!m_request)
m_request = new QNetworkRequest();
QString strURL = m_urlEdit->text();
QUrl qurl(strURL);
QString httpMethod = m_methodCombo->currentText();
m_request->setUrl(qurl);
if (httpMethod == "GET") {
QString errorString;
if (!openFile(true, errorString)) {
QMessageBox::warning(this, "Save File Error", errorString, QMessageBox::Ok);
resetState();
return;
}
m_reply = m_nam.get(*m_request);
} else if (httpMethod == "POST") {
QString errorString;
if (!openFile(false, errorString)) {
QMessageBox::warning(this, "Upload File Error", errorString, QMessageBox::Ok);
resetState();
return;
}
m_reply = m_nam.post(*m_request, m_file);
m_request->setHeader(QNetworkRequest::ContentTypeHeader, get_content_type(m_strFilePath.data()));
} else if (httpMethod == "HEAD")
m_reply = m_nam.head(*m_request);
else if (httpMethod == "DELETE")
m_reply = m_nam.deleteResource(*m_request);
else {
QMessageBox::information(this, "information", "Please select a Method!", QMessageBox::Ok);
resetState();
return;
}
connect(m_reply, SIGNAL(finished()), this, SLOT(onFinished()));
connect(m_reply, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)),
this, SLOT(onDownloadProgress(qint64, qint64)));
connect(m_reply, SIGNAL(uploadProgress(qint64, qint64)),
this, SLOT(onUploadProgress(qint64, qint64)));
connect(m_reply, SIGNAL(metaDataChanged()), this, SLOT(onMetaDataChanged()));
connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onFinished()));
m_stateText->setText(requestString(m_request));
m_stateText->append("\n===========response==========\n");
}
void Widget::onMetaDataChanged() {
m_stateText->setText(responseString(m_reply));
}
void Widget::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
if (bytesTotal > 0) {
int value = (bytesReceived * 100) / bytesTotal;
m_progress->setValue(value);
}
}
void Widget::onUploadProgress(qint64 bytesSent, qint64 bytesTotal) {
if (bytesTotal > 0) {
int value = (bytesSent * 100) / bytesTotal;
m_progress->setValue(value);
}
}
void Widget::onReadyRead() {
QByteArray data = m_reply->readAll();
m_stateText->append(QString("read %1 bytes\n").arg(data.length()));
if (m_file) {
m_file->write(data);
}
}
void Widget::onFinished() {
QString strState = m_reply->error() == QNetworkReply::NoError ? "success" : m_reply->errorString();
m_stateText->append(QString("\nhttp request done - %1\n").arg(strState));
m_stateText->append("\n-------\n");
resetState();
}
void Widget::resetState() {
if (m_file) {
m_file->close();
delete m_file;
m_file = 0;
}
if (m_request) {
delete m_request;
m_request = 0;
}
if (m_reply) {
m_reply->disconnect(this);
m_reply->deleteLater();
m_reply = 0;
}
}
函数——strrchr()
const char* strrchr(const char* str, int character);
char* strrchr(char* str, int character);
Locate last occurrence of character in string.
Returns a pointer to the last occurrence of character in the C string str.
The terminating null-character is considered part of the C string. Therefore, it can also be located to retrieve a pointer to the end of a string.
Parameters
str - C string.
character - Character to be located. It is passed as its int promotion, but it is internally converted back to char.
Return Value
A pointer to the last occurrence of character in str.
If the character is not found, the function returns a null pointer.
完整代码运行后,如下图所示:
➼ ➼ ➼ 【完整代码】