【Qt之QNetworkAccessManager】概述及示例

概述

QNetworkAccessManager类允许应用程序发送网络请求和接收应答
网络访问API是围绕一个QNetworkAccessManager对象构建的,该对象为它发送的请求保存通用配置和设置。它包含代理和缓存配置,以及与此类问题相关的信号,以及可用于监视网络操作进展的应答信号。对于整个Qt应用程序,一个QNetworkAccessManager应该足够了。
一旦创建了QNetworkAccessManager对象,应用程序就可以使用它通过网络发送请求。提供了一组标准函数,它们接受一个请求和可选数据,每个函数返回一个QNetworkReply对象。返回的对象用于获取响应相应请求而返回的任何数据。
以下是一个简单示例:

    QNetworkAccessManager *manager = new QNetworkAccessManager(this);connect(manager, &QNetworkAccessManager::finished,this, [=](QNetworkReply* reply){ui->textBrowser->append(QString(reply->readAll()));});manager->get(QNetworkRequest(QUrl("http://qt-project.org")));

get()完成后,会触发QNetworkAccessManager::finished信号。
显示如下:
在这里插入图片描述

QNetworkAccessManager有一个异步API。当触发finished(QNetworkReply*)信号时,它对应的槽函数接受的参数是QNetworkReply对象,该对象包含下载的数据以及元数据(报头等)。

注意:请求完成后,应该在适当的时候删除QNetworkReply对象。不要直接在连接finished()的槽内删除。可以使用deleteLater()函数。

reply->deleteLater();

注意:QNetworkAccessManager对它接收到的请求进行排队。并行执行的请求数量取决于协议。目前,对于桌面平台上的HTTP协议,一个主机/端口组合并行执行6个请求。
另一个,示例如下:

    QNetworkAccessManager *manager = new QNetworkAccessManager(this);QNetworkRequest request;request.setUrl(QUrl("http://qt-project.org"));request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");QNetworkReply *reply = manager->get(request);connect(reply, &QNetworkReply::readyRead, this, [this, reply](){ui->textBrowser->append(QString(reply->readAll()));});connect(reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError err)>(&QNetworkReply::error),this, [=](QNetworkReply::NetworkError err){});connect(reply, &QNetworkReply::sslErrors,this, [=](const QList<QSslError> &errors){});

get()完成后,会返回QNetworkReply对象,该对象会触发QNetworkReply::readyRead,static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError err)>(&QNetworkReply::error),QNetworkReply::sslErrors信号。
显示如下:
在这里插入图片描述

以上error()信号为什么要写成static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError err)>(&QNetworkReply::error)这样子呢?
是因为有error函数重载,而&QNetworkReply::error并不知道触发的是哪个信号,所以需要进行转换。

网络和漫游支持

  1. QNetworkAccessManager的管理能力
    • QNetworkAccessManager在Qt 4.7版本中获得了管理网络连接的能力。这意味着它不仅仅是一个简单的网络请求发送工具,还可以管理网络连接的开启和关闭。
  2. 离线启动网口
    • 当设备离线时,QNetworkAccessManager可以自动启动网口,以便发送或接收网络请求。
  3. 自动终止网口
    • 如果当前进程是最后一个使用上行链路的进程,QNetworkAccessManager会自动关闭网口。在一些平台上,为了确保资源被适当地释放,即使最后一个应用程序停止使用上行链路后,系统仍可能会保持一段时间的连接,但这个宽限期已经被考虑在内。
  4. 漫游的透明处理
    • 当设备从一个网络接入点移动到另一个接入点时(例如,从一个Wi-Fi网络切换到移动数据网络),QNetworkAccessManager可以自动处理这个切换,使得应用程序无需关心网络的变更。
  5. 自动转移网络请求
    • 如果设备从一个网络接入点移动到另一个接入点,任何正在排队或挂起的网络请求都会自动转移到新的接入点。这意味着应用程序无需关心网络变更后的请求重新发送问题。
  6. 客户端无需更改
    • 使用了QNetworkAccessManager的客户端应用程序不需要进行任何修改或更改。这意味着开发者可以简单地使用这个类,而不需要担心网络连接管理的细节。
  7. 平台依赖性
    • QNetworkAccessManager中的网络和漫游支持取决于所支持的平台。这意味着不同的操作系统或硬件可能对此功能的支持程度不同。
  8. 检测特性
    • QNetworkConfigurationManager::NetworkSessionRequired是一个方法,可以用来检测QNetworkAccessManager是否利用了这个特性。这可以帮助开发者确定是否需要针对特定的平台进行额外的处理或调整。

如:

    QNetworkConfigurationManager nCM;qDebug().noquote() << nCM.capabilities();

在这里插入图片描述
总而言之,言而总之,Qt 4.7中的QNetworkAccessManager为开发者提供了一个更简单、更自动化的网络连接管理方式,使得开发者可以更加专注于应用程序的功能和逻辑,而无需关心网络的底层细节。

常用函数

以下函数是在不同的Qt版本被引入的,因此,用到时,可以查阅当前版本的帮助手册。

枚举:enum QNetworkAccessManager::NetworkAccessibility

指示网络是否可通过此网络访问管理器访问。它用于表示应用程序的网络访问状态。enum QNetworkAccessManager::NetworkAccessibility为Qt应用程序提供了一个简单而有效的方式来检测和响应网络状态的变化。

常量描述
QNetworkAccessManager::UnknownAccessibility-1无法确定网络的可访问性。
QNetworkAccessManager::NotAccessible0网络当前不可访问,因为当前没有网络覆盖,或者通过调用setNetworkAccessible()显式禁用了网络访问。
QNetworkAccessManager::Accessible1网络可达。

该枚举作用:

  1. 网络状态检测:通过这个枚举,应用程序可以查询当前的网络连接状态,例如是否在线、离线等。
  2. 网络状态变化通知:当网络状态发生变化时,QNetworkAccessManager可以发出信号通知应用程序,使其能够响应这些变化,例如重新尝试之前失败的网络请求。
  3. 控制网络相关的功能:根据当前的网络状态,应用程序可以决定是否启用或禁用某些网络相关的功能。

其用法:

  1. 查询网络状态
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QNetworkAccessManager::NetworkAccessibility accessibility = manager->networkAccessibility();
  1. 连接网络状态变化的信号
    当网络状态发生变化时,networkAccessibleChanged信号会被发射。你可以连接这个信号来响应网络状态的变化。
connect(manager, &QNetworkAccessManager::networkAccessibleChanged, this, &YourClass::handleNetworkAccessibilityChange);

然后在槽函数中处理网络状态的变化:

void YourClass::handleNetworkAccessibilityChange(QNetworkAccessManager::NetworkAccessibility accessible) {switch (accessible) {case QNetworkAccessManager::UnknownAccessibility:// 处理未知状态break;case QNetworkAccessManager::NotAccessible:// 处理不可访问状态break;case QNetworkAccessManager::Accessible:// 处理可访问状态break;}
}
  1. 根据网络状态决策
    基于当前的网络状态,决定是否进行某些操作,例如:
if (manager->networkAccessibility() == QNetworkAccessManager::Accessible) {// 执行需要网络连接的操作
} else {// 执行离线操作或提示用户检查网络连接
}
枚举:enum QNetworkAccessManager::Operation

应答正在处理的操作。用于表示QNetworkAccessManager所执行的不同类型的网络操作。

常量描述
QNetworkAccessManager::HeadOperation1检索标头操作(使用head()创建)
QNetworkAccessManager::GetOperation2检索标题和下载内容(使用get()创建)
QNetworkAccessManager::PutOperation3上传内容操作(使用put()创建)
QNetworkAccessManager::PostOperation4通过HTTP POST(使用POST()创建)发送HTML表单的内容以进行处理
QNetworkAccessManager::DeleteOperation5删除内容操作(由deleteResource()创建)
QNetworkAccessManager::CustomOperation6自定义操作(使用sendCustomRequest()创建)

其作用是:

  1. 标识网络操作类型:通过Operation枚举,可以明确地标识和区分不同类型的网络操作,例如GET请求、POST请求等。
  2. 网络操作的管理和调度:QNetworkAccessManager使用Operation枚举来管理和调度不同的网络请求。根据操作的类型,它可以执行相应的处理逻辑和资源管理。
  3. 错误处理和重试策略:当出现网络错误时,可以根据操作的类型采取不同的错误处理策略或重试策略。
QNetworkConfiguration QNetworkAccessManager::activeConfiguration() const

返回当前活动的网络配置。

如果configuration()返回的网络配置是QNetworkConfiguration:: servicennetwork类型的,这个函数将返回该配置的当前活动子网络配置。否则返回与configuration()相同的网络配置。

使用此函数返回网络会话当前正在使用的实际网络配置。

与该函数相关的还有两个函数:
QNetworkConfiguration QNetworkAccessManager::configuration() const
void QNetworkAccessManager::setConfiguration(const QNetworkConfiguration &config)
设置将在创建要配置的网络会话时使用的网络配置。
网络配置用于在处理任何需要网络访问的请求之前创建和打开网络会话。如果没有通过此函数显式设置网络配置,则使用QNetworkConfigurationManager::defaultConfiguration()返回的网络配置。
要恢复默认网络配置,请将网络配置设置为QNetworkConfigurationManager::defaultConfiguration()返回的值。
设置网络配置意味着QNetworkAccessManager实例将只使用指定的一个。特别是,如果默认网络配置发生变化(例如Wifi可用),则需要手动启用新配置。

QNetworkConfigurationManager manager; networkAccessManager->setConfiguration(manager.defaultConfiguration());

如果设置了无效的网络配置,则不会创建网络会话。在这种情况下,无论如何都会处理网络请求,但可能会失败。例如:

networkAccessManager→setConfiguration (QNetworkConfiguration ()); 
void QNetworkAccessManager::addStrictTransportSecurityHosts(const QVector<QHstsPolicy> &knownHosts)

在HSTS缓存中添加HTTP严格传输安全策略。
注意:过期的策略将从缓存中删除已知主机(如果以前存在)。
注意:在处理HTTP响应时,QNetworkAccessManager还可以更新HSTS缓存,删除或更新现有策略或引入新的knownHosts。因此,当前的实现是服务器驱动的,客户端代码可以为QNetworkAccessManager提供以前已知或发现的策略,但是该信息可以被“Strict-Transport-Security”响应覆盖。
其作用是向已知的严格传输安全性 (Strict Transport Security, HSTS) 主机列表中添加主机。并且告诉 QNetworkAccessManager 只使用 HTTPS 与特定的主机通信,以确保通信的安全性。通过将主机添加到已知的主机列表中,浏览器将只使用 HTTPS 与这些主机通信,从而防止攻击。 这个函数在Qt 5.9`版本才被使用。

void QNetworkAccessManager::setCache(QAbstractNetworkCache *cache)

将管理器的网络缓存设置为指定的缓存。缓存用于管理器调度的所有请求。
使用此函数将网络缓存对象设置为实现附加功能的类,例如将cookie保存为永久存储。
注意:QNetworkAccessManager获取缓存对象的所有权。
默认情况下,QNetworkAccessManager没有设置缓存。Qt提供了一个简单的磁盘缓存,QNetworkDiskCache,可以使用。
对应有个cache()函数,获取返回用于存储从网络获得的数据的缓存。。

void QNetworkAccessManager::clearAccessCache()

刷新身份验证数据和网络连接的内部缓存。
这个函数在进行自动测试时很有用。

void QNetworkAccessManager::clearConnectionCache()

刷新网络连接的内部缓存。与clearAccessCache()相比,身份验证数据被保留。

void QNetworkAccessManager::connectToHost(const QString &hostName, quint16 port= 80)

启动到端口port上的主机名指定的主机的连接。此函数有助于在发出HTTP请求之前完成与主机的TCP握手,从而降低网络延迟。
注意:这个函数不可能报告错误。

void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quint16 port= 443,const QSslConfiguration &sslConfiguration = QSslConfiguration::defaultConfiguration())

使用sslConfiguration启动到端口port上由hostName指定的主机的连接。此函数有助于在发出HTTPS请求之前完成与主机的TCP和SSL握手,从而降低网络延迟。
注意:预连接SPDY连接可以通过使用允许的协议列表中包含的QSslConfiguration::NextProtocolSpdy3_0在sslConfiguration上调用setAllowedNextProtocols()来完成。当使用SPDY时,每台主机一个连接就足够了,也就是说,每台主机多次调用此方法不会导致更快的网络事务。
注意:这个函数不可能报告错误。

QNetworkCookieJar *QNetworkAccessManager::cookieJar() const

返回QNetworkCookieJar,用于存储从网络获得的cookie以及即将发送的cookie

QNetworkReply *QNetworkAccessManager::deleteResource(const QNetworkRequest &request)

发送一个删除由请求的URL标识的资源的请求。
注意:此功能目前仅适用于HTTP,执行HTTP DELETE请求。
除此之外,还有以下常用函数:
1.get(const QNetworkRequest &request):
发送一个 HTTP GET 请求到指定的 URL。
发布一个请求来获取目标请求的内容,并返回一个新的QNetworkReply对象,该对象打开以供读取,每当新数据到达时,该对象就会发出readyRead()信号。

内容以及相关的标题将被下载。
2.head(const QNetworkRequest &request):
发送一个 HTTP HEAD 请求到指定的 URL。HEAD 请求与 GET 请求类似,但不返回消息体。
发送一个请求来获取请求的网络头,并返回一个新的QNetworkReply对象,该对象将包含这些头。

该函数以关联的HTTP请求(HEAD)命名。
3. isStrictTransportSecurityEnabled() const:
4. 返回一个布尔值,指示是否启用了严格的传输安全性 (HSTS)。
5. networkAccessible() const: 返回当前的网络可访问性状态。
6. post(const QNetworkRequest &request, QIODevice *data):
发送一个 HTTP POST 请求到指定的 URL,并将数据设备的内容作为请求体发送。
向request指定的目的地发送一个HTTP POST请求,并返回一个新的QNetworkReply对象,该对象将包含服务器发送的应答。数据设备的内容将被上传到服务器。

数据必须打开以供读取,并且必须在finished()信号发出之前保持有效。

注意:在HTTP和HTTPS以外的协议上发送POST请求是未定义的,并且可能会失败。

7.post(const QNetworkRequest &request, const QByteArray &data):
8. 发送一个 HTTP POST 请求到指定的 URL,并将字节数组的内容作为请求体发送。
9. post(const QNetworkRequest &request, QHttpMultiPart *multiPart): 发送一个 HTTP POST 请求到指定的 URL,并使用 QHttpMultiPart 对象作为请求体。
10. proxy() const: 返回当前设置的代理。
返回使用此QNetworkAccessManager对象发送的请求将使用的QNetworkProxy。默认值为“QNetworkProxy::DefaultProxy”

11.proxyFactory() const: 返回当前使用的代理工厂。
12.put(const QNetworkRequest &request, QIODevice *data): 发送一个 HTTP PUT 请求到指定的 URL,并将数据设备的内容作为请求体发送。
将数据的内容上传到目标请求,并返回一个新的QNetworkReply对象,该对象将为应答打开。

当调用此函数时,必须打开数据以供读取,并且必须在发出finished()信号之前保持有效。

是否有任何内容可用于从返回对象中读取取决于协议。对于HTTP,服务器可能会发送一个小的HTML页面,表明上传成功(或失败)。其他协议可能会在其回复中包含内容。

注意:对于HTTP,此请求将发送一个PUT请求,这是大多数服务器不允许的。表单上传机制,包括通过HTML表单上传文件的机制,都使用POST机制。
13.put(const QNetworkRequest &request, const QByteArray &data): 发送一个 HTTP PUT 请求到指定的 URL,并将字节数组的内容作为请求体发送。
14.put(const QNetworkRequest &request, QHttpMultiPart *multiPart): 发送一个 HTTP PUT 请求到指定的 URL,并使用 QHttpMultiPart 对象作为请求体。
15.sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data = Q_NULLPTR): 发送一个自定义的 HTTP 请求,使用指定的动词和可选的数据设备。
将自定义请求发送到由请求的URL标识的服务器。

根据HTTP规范向服务器发送有效的谓词是用户的责任。

此方法提供了发送动词的方法,而不是通过get()或post()等方式提供的常用动词,例如发送HTTP OPTIONS命令。

如果数据不为空,则将数据设备的内容上传到服务器;在这种情况下,数据必须打开以供读取,并且必须保持有效,直到为这个应答发出finished()信号。

注意:此功能目前仅适用于HTTP(S)。
16.sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, const QByteArray &data): 发送一个自定义的 HTTP 请求,使用指定的动词和字节数组数据。
17.sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QHttpMultiPart *multiPart): 发送一个自定义的 HTTP 请求,使用指定的动词和 QHttpMultiPart 对象。
18.setCache(QAbstractNetworkCache *cache): 设置网络访问的缓存。
19.setCookieJar(QNetworkCookieJar *cookieJar): 设置 cookie 容器,用于存储和检索 cookie 信息。
20.setNetworkAccessible(NetworkAccessibility accessible): 设置网络的可访问性状态。
21.setProxy(const QNetworkProxy &proxy): 设置网络代理。
22.setProxyFactory(QNetworkProxyFactory *factory): 设置代理工厂,用于动态生成代理对象。
23.setRedirectPolicy(QNetworkRequest::RedirectPolicy policy): 设置重定向策略。
24.setStrictTransportSecurityEnabled(bool enabled): 启用或禁用严格的传输安全性 (HSTS)。
25.strictTransportSecurityHosts() const: 返回一个包含启用 HSTS 的主机的向量。
26.supportedSchemes() const: 返回此网络访问管理器支持的协议方案列表。

信号

  1. authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator):

    • 当网络请求需要身份验证时,这个信号被触发。
    • reply 是与该请求相关的 QNetworkReply 对象。
    • authenticator 是一个 QAuthenticator 对象,可以使用它来提供用户名和密码或其他身份验证信息。

每当向服务器在交付所请求的内容之前请求身份验证时,都会发出此信号。连接到该信号的插槽应该为authenticator对象中的内容(可以通过检查应答对象来确定)填充凭据。

QNetworkAccessManager将在内部缓存凭证,并在服务器再次需要身份验证时发送相同的值,而不发出authenticationRequired()信号。如果它拒绝凭据,则将再次发出此信号。

注意:要使请求不发送凭据,就不能在authenticator对象上调用setUser()或setPassword()。这将导致finished()信号发出时带有一个带有错误AuthenticationRequiredError的QNetworkReply。

注意:不可能使用QueuedConnection连接到此信号,因为如果在信号返回时身份验证器没有填写新信息,则连接将失败。
2. encrypted(QNetworkReply *reply):

  • 当网络请求成功加密时,这个信号被触发。
  • reply 是与该请求相关的 QNetworkReply 对象。

当SSL/TLS会话成功完成初始握手时发出此信号。此时,还没有传输任何用户数据。该信号可用于在证书链上执行额外的检查,例如,当网站的证书发生更改时通知用户。应答参数指定负责哪个网络应答。如果应答不符合预期的标准,那么应该通过连接到该信号的插槽调用QNetworkReply::abort()来中止应答。可以使用QNetworkReply::sslConfiguration()方法检查正在使用的SSL配置。

在内部,QNetworkAccessManager可以打开到服务器的多个连接,以便允许它并行处理请求。这些连接可以被重用,这意味着不会发出encrypted()信号。这意味着在QNetworkAccessManager的生命周期内,您只能保证在第一次连接到站点时接收到该信号。
3. finished(QNetworkReply *reply):

  • 当网络请求完成时,无论成功还是失败,这个信号被触发。
  • reply 是与该请求相关的 QNetworkReply 对象。

该信号在等待的网络应答完成时发出。reply参数将包含一个指针,指向刚刚完成的回复。该信号与QNetworkReply::finished()信号一起发出。
注意:不要删除与该信号相连的槽位中的应答对象。使用deleteLater()。
4. networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible):

  • 当网络的可访问性状态发生变化时,这个信号被触发。
  • accessible 是当前的网络可访问性状态。
  1. preSharedKeyAuthenticationRequired(QNetworkReply *reply, QSslPreSharedKeyAuthenticator *authenticator):
  • 当需要使用预共享密钥进行身份验证时,这个信号被触发。
  • reply 是与该请求相关的 QNetworkReply 对象。
  • authenticator 是一个 QSslPreSharedKeyAuthenticator 对象,用于提供预共享密钥的身份验证信息。

如果SSL/TLS握手协商PSK密码套件,因此需要PSK身份验证,则发出此信号。应答对象是正在协商此类密码套件的QNetworkReply。

当使用PSK时,客户端必须向服务器发送有效的身份和有效的预共享密钥,以便SSL握手继续进行。应用程序可以根据需要填写传递的身份验证器对象,从而在连接到该信号的插槽中提供此信息。

注意:忽略此信号,或未能提供所需的凭据,将导致握手失败,因此连接将中止。

注意:验证者对象归应答者所有,应用程序不能删除它。
6. proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator):
* 当代理服务器需要身份验证时,这个信号被触发。
* proxy 是需要进行身份验证的代理服务器。
* authenticator 是一个 QAuthenticator 对象,你可以使用它来提供代理的身份验证信息。

每当代理请求身份验证并且QNetworkAccessManager找不到有效的缓存凭据时,就会发出此信号。连接到该信号的槽应该在authenticator对象中为代理代理填写凭据。

QNetworkAccessManager将在内部缓存凭据。下一次代理请求身份验证时,QNetworkAccessManager将自动发送相同的凭据,而不会再次发出proxyAuthenticationRequired信号。

如果代理拒绝凭据,QNetworkAccessManager将再次发出信号。
7. sslErrors(QNetworkReply *reply, const QList &errors):
* 当SSL连接出现错误时,这个信号被触发。
* reply 是与该请求相关的 QNetworkReply 对象。
* errors 是一个包含所有SSL错误的列表。

如果SSL/TLS会话在设置过程中遇到错误,包括证书验证错误,则发出此信号。errors参数包含错误列表,reply是遇到这些错误的QNetworkReply。

为了表明错误不是致命的,并且应该继续连接,应该从连接到该信号的插槽调用QNetworkReply::ignoreSslErrors()函数。如果没有调用它,则在交换任何数据(包括URL)之前将断开SSL会话。

此信号可用于向用户显示一条错误消息,指出安全性可能受到威胁,并显示SSL设置如果用户决定在分析完远程证书后继续,槽应该调用ignoreSslErrors()。

示例

以下通过QNetworkAccessManager是一个下载本地文件系统的示例。main.cpp

#include <QCoreApplication>
#include <QFile>
#include <QFileInfo>
#include <QList>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QSslError>
#include <QStringList>
#include <QTimer>
#include <QUrl>
#include <QDebug>#include <stdio.h>class DownloadManager: public QObject
{Q_OBJECTQNetworkAccessManager manager;QList<QNetworkReply *> currentDownloads;public:DownloadManager();void doDownload(const QUrl &url);QString saveFileName(const QUrl &url);bool saveToDisk(const QString &filename, QIODevice *data);public slots:void execute();void downloadFinished(QNetworkReply *reply);void sslErrors(const QList<QSslError> &errors);
};DownloadManager::DownloadManager()
{connect(&manager, SIGNAL(finished(QNetworkReply*)),SLOT(downloadFinished(QNetworkReply*)));
}void DownloadManager::doDownload(const QUrl &url)
{QNetworkRequest request(url);// 当get完成后,触发downloadFinished(QNetworkReply*)槽函数QNetworkReply *reply = manager.get(request);connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(sslErrors(QList<QSslError>)));currentDownloads.append(reply);
}QString DownloadManager::saveFileName(const QUrl &url)
{// 当存在是,后面依次追加数字QString path = url.path();QString strFileName = QFileInfo(path).fileName();QString strSuffix = QFileInfo(path).suffix();QString basename = QFileInfo(path).completeBaseName();if (basename.isEmpty())basename = "download";QString strSaveName = strFileName;if (QFile::exists(strSaveName)) {int i = 0;while (QFile::exists(basename + QString::number(i) + '.' + strSuffix))++i;strSaveName = basename + QString::number(i) + '.' + strSuffix;}return strSaveName;
}bool DownloadManager::saveToDisk(const QString &filename, QIODevice *data)
{QFile file(filename);if (!file.open(QIODevice::WriteOnly)) {fprintf(stderr, "Could not open %s for writing: %s\n",qPrintable(filename),qPrintable(file.errorString()));return false;}file.write(data->readAll());file.close();return true;
}void DownloadManager::execute()
{// 添加文件信息QStringList args;args << QString("file:///F:/haha/111.txt") << QString("file:///F:/haha/222.txt")<< QString("file:///F:/haha/333.txt") << QString("file:///F:/haha/444.txt")<< QString("file:///F:/haha/555.txt") << QString("file:///F:/haha/666.txt")<< QString("file:///F:/haha/777.txt") << QString("file:///F:/haha/888.txt")<< QString("file:///F:/haha/999.txt") << QString("file:///F:/haha/101010.txt");if (args.isEmpty()) {printf("null    faild    .");QCoreApplication::instance()->quit();return;}foreach (QString arg, args) {// 转换成urlQUrl url = QUrl::fromEncoded(arg.toLocal8Bit());doDownload(url);}
}void DownloadManager::sslErrors(const QList<QSslError> &sslErrors)
{foreach (const QSslError &error, sslErrors)fprintf(stderr, "SSL error: %s\n", qPrintable(error.errorString()));
}void DownloadManager::downloadFinished(QNetworkReply *reply)
{QUrl url = reply->url();if (reply->error()) {fprintf(stderr, "Download of %s failed: %s\n",url.toEncoded().constData(),qPrintable(reply->errorString()));} else {// 修改保存文件名称QString filename = saveFileName(url);// 保存if (saveToDisk(filename, reply))printf("Download of %s succeeded (saved to %s)\n",url.toEncoded().constData(), qPrintable(filename));}currentDownloads.removeAll(reply);reply->deleteLater();if (currentDownloads.isEmpty()){fprintf(stderr, "%s\n", "finished.");QCoreApplication::instance()->quit();}
}int main(int argc, char **argv)
{QCoreApplication app(argc, argv);// 声明对象DownloadManager manager;// 执行下载操作QTimer::singleShot(0, &manager, SLOT(execute()));app.exec();
}#include "main.moc"

上述示例是一个基于Qt网络模块实现的多文件下载管理器。
通过使用QNetworkAccessManager,它允许用户同时下载多个文件,并将文件保存到磁盘上。
主要实现功能:
1.创建DownloadManager类,继承自QObject,并从QNetworkAccessManager继承了manager对象。
2.doDownload函数:接受一个QUrl对象作为参数,将其封装到QNetworkRequest中,然后通过manager对象发送GET请求。
3.saveFileName函数:根据url的路径生成文件名,并保证文件名不重复。
4.saveToDisk函数:将下载的文件保存到磁盘。
5.execute函数:执行下载操作,通过添加文件信息到QStringList中,循环调用doDownload函数实现同时下载多个文件。
6.sslErrors函数:处理SSL错误。
7.downloadFinished函数:下载完成时进行处理,判断是否有错误发生,并执行保存文件和打印下载成功信息等操作。
8.main函数:创建QCoreApplication对象和DownloadManager对象,通过QTimer::singleShot实现异步调用执行execute函数。

效果:
在这里插入图片描述

结论

如果上帝没有给你你想要的,那不是你值得更好,而是你不值得

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/222173.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Antd Select 添加中框

默认antd 的 Select中间并没有竖框&#xff0c;但是ui design设计了&#xff0c;所以记录一下如何添加 默认&#xff1a; CSS&#xff1a; .custom-select-suffix-icon {display: flex;align-items: center; }.custom-select-suffix-icon::before {content: ;height: 31px; …

什么品牌的猫粮比较好?主食冻干猫粮品牌十大排行

咱们养猫人每天最愁的就是咋给自家猫咪选一款优质的猫粮&#xff0c;让猫主子吃了健健康康的。早些年大多养猫人的标准就是盯着进口的买&#xff0c;所以之前进口猫粮的销量一直遥遥领先&#xff0c;感觉品控也严&#xff0c;也就放心大胆的冲进口猫粮了&#xff0c;但近期百利…

flink中如何把DB大表的配置数据加载到内存中对数据流进行增强处理

背景 在处理flink的数据流时&#xff0c;比如处理商品流时&#xff0c;一般我们从kafka中只拿到了商品id&#xff0c;此时我们需要把商品的其他配置信息比如品牌品类等也拿到&#xff0c;此时就需要关联上外部配置表来达到丰富数据流的目的&#xff0c;如果外部配置表很大&…

我的隐私计算学习——隐私集合求交(1)

笔记内容来自多本书籍、学术资料、白皮书及ChatGPT等工具&#xff0c;经由自己阅读后整理而成。 &#xff08;一&#xff09;PSI的介绍 隐私计算关键技术&#xff1a;隐私集合求交&#xff08;PSI&#xff09;原理介绍 隐私计算关键技术&#xff1a;隐私集合求交&#xff08…

事务--03---TCC空回滚、悬挂、幂等解决方案

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Seata TCC 模式设计思路TCC存在的问题1、空回滚以及解决方案解决方案&#xff1a; 2、幂等问题以及解决方案解决方案&#xff1a; 3、悬挂问题以及解决方案解决方案…

PCIe设备热插拔-理论篇

硬件层面理解热插拔 PRSNT1#和PRSNT2#信号与PCIe设备的热插拔相关。在基于PCIe总线的Add-in 卡中&#xff0c;PRSNT1# 和PRSNT2#信号直接相连&#xff0c;而在处理器主板中&#xff0c;PRSNT1#信号接地&#xff0c;而PRSNT2#信号通过上 拉电阻接为高。 不同的处理器系统处理PC…

【Mysql】InnoDB的表空间(九)

概述 表空间是一个在 InnoDB 中比较抽象的概念&#xff0c;对于系统表空间来说&#xff0c;对应着文件系统中一个或多个实际文件&#xff1b;而对于每个独立表空间来说&#xff0c;对应着文件系统中一个名为表名.ibd 的实际文件。可以把表空间想象成由很多个页组成的池子&…

【Unity 实用工具篇】| 游戏多语言解决方案,官方插件Localization 实现本地化及多种语言切换

前言 【Unity 实用工具篇】| 游戏多语言解决方案&#xff0c;官方插件Localization 实现本地化及多种语言切换一、多语言本地化插件 Localization1.1 介绍1.2 效果展示1.3 使用说明 二、 插件导入并配置2.1 安装 Localization2.2 全局配置 三、多语言映射表3.1 创建多语言文本配…

字符处理 C语言xdoj52

问题描述 从键盘输入一个字符&#xff0c;若为小写字母&#xff0c;则输出其对应的大写字母&#xff1b;若为大写字母&#xff0c;则输出对应的小写字母&#xff1b;其他字符原样输出。 输入说明 输入一个字符 输出说明 输出一个字符 输入样例 样例1输入 a 样例…

分布式块存储 ZBS 的自主研发之旅|元数据管理

重点内容 元数据管理十分重要&#xff0c;犹如整个存储系统的“大黄页”&#xff0c;如果元数据操作出现性能瓶颈&#xff0c;将严重影响存储系统的整体性能。如何提升元数据处理速度与高可用是元数据管理的挑战之一。SmartX 分布式存储 ZBS 采用 Log Replication 的机制&…

论文修改润色平台 PaperBERT

大家好&#xff0c;今天来聊聊论文修改润色平台&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 标题&#xff1a;论文修改润色平台――助力学术研究&#xff0c;提升论文质量 一、引言 在学术研究中&am…

复制粘贴——QT实现原理

复制粘贴——QT实现原理 QT 剪贴板相关类 QClipboard 对外通用的剪贴板类&#xff0c;一般通过QGuiApplication::clipboard() 来获取对应的剪贴板实例。 // qtbase/src/gui/kernel/qclipboard.h class Q_GUI_EXPORT QClipboard : public QObject {Q_OBJECT private:explici…

单片机——通信协议(FPGA+c语言应用之spi协议解析篇)

引言 串行外设接口(SPI)是微控制器和外围IC&#xff08;如传感器、ADC、DAC、移位寄存器、SRAM等&#xff09;之间使用最广泛的接口之一。本文先简要说明SPI接口&#xff0c;然后介绍ADI公司支持SPI的模拟开关与多路转换器&#xff0c;以及它们如何帮助减少系统电路板设计中的数…

ChatGLM大模型推理加速之Speculative Decoding

目录 一、推测解码speculative decoding 1、自回归解码 2、speculative decoding 3、细节理解 二、核心逻辑代码 1、算法流程代码 2、模型自回归代码 a、带缓存的模型自回归实现代码 b、优化版本带缓存的模型自回归实现代码 c、ChatGLM的past_key_values的回滚 三、…

EM的理论基础

1 EM定义​ 电迁移(Electro-Migration)是指在外加电场下,电子和金属原子之间的动量转移导致材料的运动。这种动量传递导致金属原子(比如Cu原子)从其原始位置移位,如图7-1。这种效应随着导线中电流密度的增加而增加,并且在更高的温度下,动量传递变得更加严重。因此,在先…

[WMCTF2020]Make PHP Great Again require_once 特性

php源码分析 require_once 绕过不能重复包含文件的限制-安全客 - 安全资讯平台 这里是特性 我们首先来解释一下 <?php highlight_file(__FILE__); require_once flag.php; if(isset($_GET[file])) {require_once $_GET[file]; }这个是我们的源代码 PHP包含的格式是将 已…

考验的是技术!如何绕过微软设计的安装Windows 11的硬件要求

这篇文章解释了绕过微软设计的硬件要求的三种方法,允许你在几乎任何电脑上安装Windows 11。 注意:绕过Windows 11要求所涉及的一些过程需要更深入的技术知识。请不要编辑计算机的注册表,除非你对此乐此不疲,因为它可能会损坏你的设备。 绕过Windows 11要求 虽然绕过Wind…

AUTO.js连接电脑时,握手失败

使用模拟器上的autox.js连接vscode。ipv4正确&#xff0c;但总是握手失败。 检查了一下vscode安装的插件&#xff0c;最开始安装的是这个&#xff1a; 将之前安装的插件禁用。 更换这个插件&#xff1a; 然后启动服务后就可以连接成功了。

Nginx服务器配置SSL证书

本文将全面介绍如何在Nginx或Tengine服务器配置SSL证书&#xff0c;具体包括下载和上传证书文件&#xff0c;在Nginx上配置证书文件、证书链和证书密钥等参数&#xff0c;以及安装证书后结果的验证。成功配置SSL证书后&#xff0c;您将能够通过HTTPS加密通道安全访问Nginx服务器…

成分党品牌

现在越来越多的成分党品牌崛起&#xff0c;主打成分护肤&#xff0c;不添加任何成分&#xff0c;其中有Timeless、The Ordinary、John Jeff、欧邦琪&#xff08;Obagi&#xff09;、修丽可&#xff08;SkinCeuticals&#xff09;、HFP 。 参考文章&#xff1a;成分党都会良心推…