【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_OBJECT
    QNetworkAccessManager 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) {
        // 转换成url
        QUrl 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函数。

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

结论

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

读书笔记-《数据结构与算法》-摘要5[归并排序]

归并排序 核心&#xff1a;将两个有序对数组归并成一个更大的有序数组。通常做法为递归排序&#xff0c;并将两个不同的有序数组归并到第三个数组中。 先来看看动图&#xff0c;归并排序是一种典型的分治应用。 public class MergeSort {public static void main(String[] ar…

Unity Mono加密解决方案

Unity Mono 是 Unity 引擎默认的脚本运行时环境&#xff0c;在游戏开发中扮演着重要的角色。Mono 由跨平台的开源 .NET 框架实现&#xff0c;它允许开发者使用 C# 等编程语言编写游戏逻辑。凭借简单易用的开发环境和高效的脚本编译速度&#xff0c;得到了众多游戏的青睐。 在 …

C语言数据结构-二叉树的入门

文章目录 0 碎碎念1 二叉树的概念和结构1.1 概念和特点1.2 结构1.3 特殊的二叉树1.4 二叉树的存储与性质1.5 前序、中序和后序 2 简单二叉树的实现2.1 定义数据结构类型2.2 前序、中序和后序接口的实现2.3 二叉树中节点的个数2.4 叶子节点的个数 3 完整代码块3.1 BinaryTree.h3…

Pycharm2023安装

PyCharm是一种Python IDE&#xff08;集成开发环境&#xff09;&#xff0c;带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具&#xff0c;比如调试、语法高亮、项目管理、代码跳转、智能提示、自动完成、单元测试、版本控制。此外&#xff0c;该IDE提供了一些高…

亚马逊云科技发布企业生成式AI助手Amazon Q,助力企业迈向智能化时代

&#xff08;声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 亚马逊云科技开发者社区、知乎、自媒体平台、第三方开发者媒体等亚马逊云科技官方渠道&#xff09; 一、前言 随着人工智能技术的快速发展和广泛应用&#xff0c;我们…

WTF ‘Questions‘

WTF ‘Tech Team Lead’ As a Tech Team Lead, your role is to oversee the technical aspects of a project or team, and to provide guidance, support, and leadership to your team members. Here are some key responsibilities and aspects of the role: Leadership …

ChatGLM大模型推理加速之Speculative Decoding

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

C/C++: 数据结构之索引查找(分块查找)

画图举例&#xff1a; #include<bits/stdc.h> using namespace std; /** * * Author:HackerHao * Create:2023.12.14 * */ typedef struct {int Key;int Link; }indextype;//分块查找 int IndexSequelSearch(indextype ls[], int s[], int m, int Key) //关键字为Key, 索…

云原生架构总结-读书笔记

云原生架构进阶实战-读书笔记 云原生概念 云原生&#xff08;Cloud Native&#xff09;概念是由Pivotal的Matt Stine在2013年首次提出的。这个概念得到了社区的不断完善&#xff0c;内容越来越丰富&#xff0c;目前已经**包括了DevOps&#xff08;Development和Operations的组…

云计算:Vmware 安装 FusionCompute

目录 一、理论 1.FusionCompute 二、实验 1.Vmware 安装 FusionCompute&#xff08;CNA&#xff09; 2.Vmware 安装 FusionCompute&#xff08;VRM&#xff09; 三、问题 1. VRM-WEB登录失败 2.Windows cmd中无法ping通虚拟机 一、理论 1.FusionCompute &#xff08;…

LangChain(0.0.340)官方文档九:Retrieval——Text embedding models、Vector stores、Indexing

LangChain官网、LangChain官方文档 、langchain Github、langchain API文档、llm-universe 文章目录 一、Text embedding models1.1 Embeddings类1.2 OpenAI1.3 Sentence Transformers on Hugging Face1.4 CacheBackedEmbeddings1.4.1 简介1.4.2 与Vector Store一起使用1.4.3 内…

保障事务隔离级别的关键措施

目录 引言 1. 锁机制的应用 2. 多版本并发控制&#xff08;MVCC&#xff09;的实现 3. 事务日志的记录与恢复 4. 数据库引擎的实现策略 结论 引言 事务隔离级别是数据库管理系统&#xff08;DBMS&#xff09;中的一个关键概念&#xff0c;用于控制并发事务之间的可见性。…

TikTok与虚拟现实的完美交融:全新娱乐时代的开启

TikTok&#xff0c;这个风靡全球的短视频平台&#xff0c;与虚拟现实&#xff08;VR&#xff09;技术的深度结合&#xff0c;为用户呈现了一场全新的娱乐盛宴。虚拟现实技术为TikTok带来了更丰富、更沉浸的用户体验&#xff0c;标志着全新娱乐时代的开启。本文将深入探讨TikTok…

Tomcat部署(图片和HTML等)静态资源时遇到的问题

文章目录 Tomcat部署静态资源问题图中HTML代码启动Tomcat后先确认Tomcat是否启动成功 Tomcat部署静态资源问题 今天&#xff0c;有人突然跟我提到&#xff0c;使用nginx部署静态资源&#xff0c;如图片。可以直接通过url地址访问&#xff0c;为什么他的Tomcat不能通过这样的方…

持续集成交付CICD:Jenkins使用基于SaltStack的CD流水线下载Nexus制品

目录 一、理论 1.salt常用命令 二、实验 1.SaltStack环境检查 2.Jenkins使用基于SaltStack的CD流水线下载Nexus制品 二、问题 1.salt未找到命令 2.salt简单测试报错 3. wget输出日志过长 一、理论 1.salt常用命令 &#xff08;1&#xff09;salt 命令 该 命令执行s…

回答一个同学的问题:在目前深度学习爆火的年代,专家系统还有用吗,会被淘汰吗?

文章目录 我的看法如下&#xff1a;&#xff08;不会被淘汰&#xff0c;会逐渐进化&#xff09;总结 我的看法如下&#xff1a;&#xff08;不会被淘汰&#xff0c;会逐渐进化&#xff09; 专家系统和深度学习有其各自的优势。专家系统利用规则和知识库来给出结论,适用于问题范…

There appears to be trouble with your network connection. Retrying

一直在报如上错误&#xff0c;试了很多办法&#xff0c;比如删掉yarn.lock&#xff0c;yarn cache clean&#xff0c;删掉node_modules&#xff0c;rm proxy等等都没有用 甚至于重启电脑&#xff0c;然而并没有什么用 突然间想到&#xff0c;我用了clash for window 所以想了…

Redis权限管理体系(一):客户端名及用户名

在Redis6之前的版本中&#xff0c;因安全认证的主要方式是使用Redis实例的密码进行基础控制&#xff0c;而无法按照不同的应用来源配置不同账号以及更细粒度的操作权限控制来管理。本文先从client list中的信息入手&#xff0c;逐步了解Redis的客户端名设置、用户设置及权限控制…

模型评估:压力测试 模拟对手 对齐 智能对抗 CAPTCHA(全自动区分计算机和人类的公共图灵测试)

对齐&#xff0c;智能对抗&#xff1a;魔高一尺&#xff0c;道高一丈。用更高的智能去对抗恶意使用。openAI一半的内容都在讲这个&#xff0c;但没有讲具体的方法。 如果认为对方是一个人就通过了图灵测试&#xff0c;真正的实现了智能。 如果智能达到了这种程度&#xff0c;智…

【干货分享】网工必要了解协议MPLS

热门IT技术--视频教程https://xmws-it.blog.csdn.net/article/details/134398330?spm1001.2014.3001.5502 MPLS是一种在IP骨干网上利用标签来指导数据报文高速转发的协议&#xff0c;由IETF &#xff08;Internet Engineering Task Force&#xff0c;因特网工程服务组&#xf…