QNetworkAccessManager、QNetworkRequest和QNetworkReply三者关系:
QNetworkAccessManager是主体,它负责发送和接收,也就是get和post,并且他还能判断网络连接状态,通过networkSessionConnected信号判断。
QNetworkRequest是请求,它是要访问的内容,并且它也能对这些内容进行配置,它放在QNetworkAccessManager的get和post中使用。
QNetworkReply它是回复,即返回结果,它能获取到get,post后得到的结果,包括正常的和异常的。
拓展:QSslConfiguration它是QNetworkRequest的一个访问设置类,如下设置协议类型支持https:
QNetworkRequest req(url);
QSslConfiguration config;
config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::AnyProtocol);
req.setSslConfiguration(config);
QNetworkRequest可以通过设置head来设置发送类型的格式,如下:
字符串模式:
req.setHeader(QNetworkRequest::ContentTypeHeader,
QVariant("application/x-www-form-urlencoded"));
json模式:
req.setHeader(QNetworkRequest::ContentTypeHeader,
QVariant("application/json"));
这里简单介绍下QNetworkAccessManager的get和post,它们都是访问服务器,但是get一般是不带参数的,post一般会有参数的。当然get也可以带参数,例如QNetworkRequest设置的url里面带有参数,例如这种格式:http://testurl?id=666&name=“xiaoming”;
QNetworkReply作为获取返回值和获取连接状态来使用,它可以同步获取,也可以异步获取。
同步获取:使用QEventloop来做一个非阻塞延时。
异步获取:通过关联信号readyRead来获取。
同步访问后台使用实例:
.h
#ifndef HTTPCLIENT_H
#define HTTPCLIENT_H
#include <QNetworkAccessManager>
#include <QObject>
class HttpClient : public QObject {
Q_OBJECT
public:
HttpClient(QObject *parent = 0);
QByteArray get(const QString &url, int timeout);
QByteArray post(const QString &url, const QByteArray &data, int timeout);
QByteArray posttojson(const QString &url, const QByteArray &data, int timeout);
private slots:
void replyFinished(QNetworkReply *reply);
protected:
QByteArray request(QNetworkReply *reply, int timeout);
private:
QNetworkAccessManager *m_manager;
QString m_recvData;
};
#endif // HTTPCLIENT_H
cpp:
#include "httpclient.h"
#include <QDebug>
#include <QEventLoop>
#include <QNetworkReply>
#include <QSslConfiguration>
#include <QTextCodec>
#include <QTimer>
#define HTTP_HEADER_KEY "X-Version"
#define HTTP_HEADER_VALUE "1.0"
HttpClient::HttpClient(QObject *parent) : QObject(parent) {
m_recvData = "";
m_manager = new QNetworkAccessManager(this);
connect(m_manager, SIGNAL(finished(QNetworkReply *)), this,
SLOT(replyFinished(QNetworkReply *)));
}
QByteArray HttpClient::get(const QString &url, int timeout) {
QNetworkRequest req(url);
//下边的四行代码是做验证处理的,方式不止这一种
QSslConfiguration config;
config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::AnyProtocol);
req.setSslConfiguration(config);
req.setRawHeader(HTTP_HEADER_KEY,
HTTP_HEADER_VALUE);
req.setHeader(QNetworkRequest::ContentTypeHeader,
QVariant("application/x-www-form-urlencoded"));
return request(m_manager->get(req), timeout);
}
QByteArray HttpClient::post(const QString &url, const QByteArray &data,
int timeout) {
QNetworkRequest req(url);
//下边的四行代码是做验证处理的,方式不止这一种
QSslConfiguration config;
config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::AnyProtocol);
req.setSslConfiguration(config);
req.setRawHeader(HTTP_HEADER_KEY,
HTTP_HEADER_VALUE);
req.setHeader(QNetworkRequest::ContentTypeHeader,
QVariant("application/x-www-form-urlencoded"));
return request(m_manager->post(req, data), timeout);
}
QByteArray HttpClient::posttojson(const QString &url, const QByteArray &data,
int timeout) {
QNetworkRequest req(url);
//下边的四行代码是做验证处理的,方式不止这一种
QSslConfiguration config;
config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::AnyProtocol);
req.setSslConfiguration(config);
req.setRawHeader(HTTP_HEADER_KEY,
HTTP_HEADER_VALUE);
req.setHeader(QNetworkRequest::ContentTypeHeader,
QVariant("application/json"));
return request(m_manager->post(req, data), timeout);
}
void HttpClient::replyFinished(QNetworkReply *reply) {
QVariant status_code =
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
QByteArray bytes = reply->readAll();
QTextCodec *pCodec = QTextCodec::codecForName("UTF-8");
m_recvData = pCodec->toUnicode(bytes);
if (reply->error() != QNetworkReply::NoError) {
qWarning() << "http通信返回错误" <<reply->url()<<status_code;
}
reply->deleteLater(); //要删除reply,要调用deletelater;
}
QByteArray HttpClient::request(QNetworkReply *reply, int timeout) {
QEventLoop loop;
QTimer timer;
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
timer.start(timeout);
loop.exec();
return m_recvData.toUtf8();
}
get调用:
HttpClient httpClient;
QString strUrl = QString("%1?test1=%2&test2=%3").arg(url).arg("1").arg("2");
QByteArray resData = httpClient.get(strUrl, 3000);
post调用:
QString data = QString("test1=%1&test2=%2").arg("1").arg("2");
HttpClient httpClient;
QString strUrl = "https://api.test.com/parm";
QByteArray resData = httpClient.post(strUrl, data.toUtf8(), 3000);
posttojson调用:
QJsonDocument jsonDoc;
QJsonObject jsonObj;
jsonObj.insert("test1","1");
jsonObj.insert("test2","2");
jsonDoc = QJsonDocument(jsonObj);
resData = httpClient.posttojson(strUrl, jsonDoc.toJson(), 3000);
从服务器下载文件实例:
.h
#ifndef HTTPDOWNLOADBUSSSINESS_H
#define HTTPDOWNLOADBUSSSINESS_H
#include <QObject>
#include <QFile>
#include <QNetworkReply>
#include <QObject>
class Reply;
class HttpDownloadBussiness : public QObject {
Q_OBJECT
public:
HttpDownloadBussiness (QString url,QString fileName,QObject *parent = 0);
// bool getRunningStatus() const;
public slots:
bool download();
// bool stopDownload(QString url);
public slots:
void handleFinished();
void handleReadyRead();
void updateDownloadProgress(qint64, qint64);
void handleError(QNetworkReply::NetworkError error);
private:
bool raiseFailed();
void raiseCompleted();
// void raiseStop();
signals:
void progressChangedSig(quint64 p, quint64 t);
void downloadCompletedSig();
void downloadFailedSig();
// void downloadStopSig();
private:
QNetworkAccessManager *m_manager;
QNetworkReply *m_netReply;
Reply *m_reply;
quint64 m_total;
bool m_isRunning;
QFile *m_file;
QUrl m_url;
QString m_fileTemp;
qint64 m_existSize;
// bool m_isStop;
};
class Reply : public QObject {
Q_OBJECT
public:
Reply(QNetworkReply *reply);
enum { Head, Get };
int method() const { return m_method; }
void setMethod(int method) { m_method = method; }
void abort() { m_reply->abort(); }
QVariant header(QNetworkRequest::KnownHeaders header) const {
return m_reply->header(header);
}
QNetworkReply *reply() const { return m_reply; }
quint64 current;
signals:
void finished();
void errorOccurred(QNetworkReply::NetworkError);
void readyRead();
void updateDownloadProgress(qint64, qint64);
private:
QNetworkReply *m_reply;
int m_method;
};
#endif // HTTPDOWNLOADBUSSSINESS_H
.cpp
#include "httpdownloadbussiness.h"
#include <QDebug>
#include <QFile>
#include <QSslConfiguration>
#include <QThread>
#include <QTimerEvent>
HttpDownloadBussiness ::HttpDownloadBussiness(QString url,QString fileName,QObject *parent) : QObject(parent) {
m_isRunning = false;
m_url = QUrl(url);
m_fileTemp = fileName + "_tmp";
// m_isStop = false;
}
bool HttpDownloadBussiness ::download() {
m_manager = new QNetworkAccessManager;
m_isRunning = true;
m_existSize = 0;
m_file = new QFile(m_fileTemp, this);
if (!m_file->open(QFile::WriteOnly | QIODevice::Append)) {
raiseFailed();
return false;
}
QNetworkRequest req(m_url);
//下边的四行代码是做验证处理的,方式不止这一种
QSslConfiguration config;
config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::AnyProtocol);
req.setSslConfiguration(config);
QNetworkAccessManager *accessManager = new QNetworkAccessManager(this);
qDebug() << "当前支持协议:"<<accessManager->supportedSchemes();
// req.setRawHeader("Range", pos.toLatin1());
// if (m_manager->networkAccessible() == QNetworkAccessManager::NotAccessible) {
// m_manager->setNetworkAccessible(QNetworkAccessManager::Accessible);
// }
m_reply = new Reply(m_manager->get(req));
m_reply->current = 0;
qWarning() << QString::fromUtf8("%1文件开始下载...").arg(m_fileTemp)<<m_url;
connect(m_reply, SIGNAL(readyRead()), this, SLOT(handleReadyRead()));
connect(m_reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this,
SLOT(handleError(QNetworkReply::NetworkError)));
connect(m_reply, SIGNAL(finished()), this, SLOT(handleFinished()));
connect(m_reply,SIGNAL(finished()), m_reply, SLOT(deleteLater()));
connect(m_reply, SIGNAL(updateDownloadProgress(qint64, qint64)), this,
SLOT(updateDownloadProgress(qint64, qint64)));
return true;
}
#if 0
bool HttpDownloadBussiness ::stopDownload(QString url) {
if (m_url == QUrl(url)) {
m_isRunning = false;
if (m_reply && !m_isStop) {
disconnect(m_reply, SIGNAL(readyRead()), this, SLOT(handleReadyRead()));
disconnect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
SLOT(handleError(QNetworkReply::NetworkError)));
disconnect(m_reply, SIGNAL(finished()), this, SLOT(handleFinished()));
disconnect(m_reply, SIGNAL(finished()), m_reply, SLOT(deleteLater()));
disconnect(m_reply, SIGNAL(updateDownloadProgress(qint64, qint64)), this,
SLOT(updateDownloadProgress(qint64, qint64)));
if (!m_isStop)
{
m_reply->reply()->abort();
if (m_reply)
m_reply->reply()->deleteLater();
m_isStop = true;
}
}
emit downloadStopSig();
return true;
}
return false;
}
bool HttpDownloadBussiness ::getRunningStatus() const { return m_isRunning; }
#endif
void HttpDownloadBussiness ::handleFinished() {
if (m_isRunning)
{
// m_isStop = true;
m_file->close();
int index = m_fileTemp.lastIndexOf("_tmp");
QString realName = m_fileTemp.left(index);
bool ok = QFile::rename(m_fileTemp, realName);
qWarning() <<__FUNCTION__ << __LINE__<<QStringLiteral("下载完成:")<<m_fileTemp;
raiseCompleted();
}
}
void HttpDownloadBussiness ::handleReadyRead() {
Reply *reply = qobject_cast<Reply *>(sender());
QByteArray content = reply->reply()->readAll();
m_file->seek(reply->current);
m_file->write(content);
reply->current += content.length();
}
void HttpDownloadBussiness ::updateDownloadProgress(qint64 part, qint64 total) {
// qDebug()<<QStringLiteral("下载进度:")<<(part+m_existSize)/1024.00/1024.00<<QStringLiteral("总大小:")<<(total+m_existSize)/1024.00/1024.00;
if (m_isRunning)
emit progressChangedSig(part + m_existSize, total + m_existSize);
}
void HttpDownloadBussiness ::handleError(QNetworkReply::NetworkError error) {
qDebug() << "http error:" << error;
if (error != QNetworkReply::NoError && error != QNetworkReply::UnknownContentError) {
qDebug() << "http error:" << error;
m_isRunning = false;
m_file->close();
//暂时屏蔽该代码,使用恢复下载超时处理,
if (!QFile::remove(m_file->fileName())) {
qDebug() << "remove failed";
}
}
emit downloadFailedSig();
}
bool HttpDownloadBussiness ::raiseFailed() {
m_isRunning = false;
m_file->close();
if (!QFile::remove(m_file->fileName())) {
qDebug() << "create remove failed";
}
emit downloadFailedSig();
return false;
}
void HttpDownloadBussiness ::raiseCompleted() {
m_isRunning = false;
connect(m_manager, SIGNAL(finished(QNetworkReply *)), m_manager,
SLOT(deleteLater()));
emit downloadCompletedSig();
}
#if 0
void HttpDownloadBussiness ::raiseStop() { emit downloadStopSig(); }
#endif
Reply::Reply(QNetworkReply *reply) {
m_reply = reply;
qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
connect(m_reply, SIGNAL(finished()), this, SIGNAL(finished()));
connect(m_reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this,
SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
connect(m_reply, SIGNAL(readyRead()), this, SIGNAL(readyRead()));
connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this,
SIGNAL(updateDownloadProgress(qint64, qint64)));
}
下面介绍一下使用QHttpMultiPart 和QHttpPart 来上传文件的方法。
void testWidget::on_uploadBtn_clicked()
{
if(_pUploadManager == nullptr)
_pUploadManager = new QNetworkAccessManager(this);
QString uploadFileName = QApplication::applicationDirPath()+"/exportLog/";
uploadFileName = uploadFileName+ui->uploadbox->currentText();
QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("multipart/form-data"));
QString nameUrl = QString("form-data; name=\"file\"; filename=\"%1\"").arg(ui->uploadbox->currentText());
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(nameUrl));
QFile file(uploadFileName);
if (!file.open(QIODevice::ReadOnly))
{
qDebug()<<"open http file fail";
return;
}
filePart.setBody(file.readAll());
file.close();
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
multiPart->append(filePart);
QNetworkRequest req(QUrl(URLHEAD));
QSslConfiguration config;
config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::AnyProtocol);
req.setSslConfiguration(config);
_pReply = _pUploadManager->post(req, multiPart);
qDebug()<<"upload filename"<<uploadFileName<<nameUrl;
//设置父对象为QNetworkReply,方便QNetworkReply销毁时把QHttpMultiPart销毁
multiPart->setParent(_pReply);
connect(_pReply, &QNetworkReply::finished,this, [&](){
QVariant status_code = _pReply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
qDebug()<<"upload finish"<<status_code<<_pReply->readAll();
if (_pReply->error() != QNetworkReply::NoError) {
qWarning() << "http通信返回错误" <<_pReply->url()<<status_code;
}
_pReply->deleteLater(); //要删除reply,要调用deletelater;
});
connect(_pReply, &QNetworkReply::uploadProgress, this, [&](qint64 bytes, qint64 total){
qDebug()<<"upload:"<<bytes<<total;
});
connect(_pReply, SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(slotHttpError(QNetworkReply::NetworkError)));
}