【Qt之QNetworkAccessManager】概述及示例

news2024/11/22 15:30:30

概述

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函数。

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

结论

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

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

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

相关文章

【Linux API 揭秘】container_of函数详解

我的圈子&#xff1a; 高级工程师聚集地 我是董哥&#xff0c;高级嵌入式软件开发工程师&#xff0c;从事嵌入式Linux驱动开发和系统开发&#xff0c;曾就职于世界500强企业&#xff01; 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; …

leetcode-21-合并两个有序链表(C语言实现)

题目&#xff1a; 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输出…

不用再找,这是大模型 LLM 微调经验最全总结

大家好&#xff0c;今天对大模型微调项目代码进行了重构&#xff0c;支持ChatGLM和ChatGLM2模型微调的切换&#xff0c;增加了代码的可读性&#xff0c;并且支持Freeze方法、Lora方法、P-Tuning方法、「全量参数方法」 微调。 PS&#xff1a;在对Chat类模型进行SFT时&#xff…

一款计算机顶会爬取解析系统 paper info

一款计算机顶会爬取解析系统 paper info 背景项目实现的功能 技术方案架构设计项目使用的技术选型 使用方法本地项目部署使用ChatGPT等大模型创建一个ChatGPT助手使用阿里云 顶会数据量 百度网盘pfd文件json文件 Q&A github链接 &#xff1a;https://github.com/codebricki…

PyTorch 模型训练性能大揭秘:从基础到高级技巧一网打尽!

PyTorch 是一个开源的 Python 机器学习库&#xff0c;基于Torch&#xff0c;用于自然语言处理等应用程序。 PyTorch既可以看作加入了GPU支持的numpy&#xff0c;也可以看成一个拥有自动求导功能的强大的深度神经网络&#xff0c;同时它也是大模型开发的首选工具。 《PyTorch模…

每日分享,以元旦为题的诗词

元旦佳节即将来临&#xff0c;相信大家都会在朋友圈表达一下自己的情感&#xff0c;不管大家以前是怎么表达的&#xff0c;今天小编给你分享几首以元旦为题的几首诗&#xff0c;喜欢的朋友可以自取&#xff0c;想要更多免费的诗词&#xff0c;请自行百度或小程序搜索&#xff1…

ES中根据主键_id查询记录

一、需求 es中_type&#xff1a;_doc&#xff0c;想要根据主键_id查询记录 二、实现 复合查询中使用语句查询http://192.168.1.1/_doc/1

ArcGIS for Android开发引入arcgis100.15.2

最后再点击同步即可&#xff01;&#xff01;&#xff01;

Stable diffusion 简介

Stable diffusion 是 CompVis、Stability AI、LAION、Runway 等公司研发的一个文生图模型&#xff0c;将 AI 图像生成提高到了全新高度&#xff0c;其效果和影响不亚于 Open AI 发布 ChatGPT。Stable diffusion 没有单独发布论文&#xff0c;而是基于 CVPR 2022 Oral —— 潜扩…

如何用开关电源测试系统测试电源峰值电流?

一、用万用表、示波器测量峰值电流 首先将待测电路输入信号线分别连接到测试电路的输入端和地端。待测电路的电源端连接电源。然后将示波器设置为AC耦合模式&#xff0c;通道1连接待测电路输入端&#xff0c;通道2连接待测电路地端。调整数字万用表为电流测量模式。打开电源&am…

【动手学深度学习】(十三)深度学习硬件

文章目录 一、CPU和GPU二、更多的芯片1.DSP:数字信号处理2.可编程阵列(FPGA)3.AI ASIC 三、单机多卡并行 一、CPU和GPU 提升CPU利用率 在计算ab之前&#xff0c;需要准备数据 主内存->L3->L2->L1->寄存器(数据只有进入寄存器才可以参与运算) 提升空间和时间的内存…

USB2.0 Spec 中文篇

体系简介 线缆 USB 是一种支持热拔插的高速串行传输总线&#xff0c;使用一对&#xff08;两根&#xff09;差分信号来传输数据&#xff0c;半双工。要求使用屏蔽双绞线。 供电 USB 支持 “总线供电” 和 “自供电” 两种供电模式。在总线供电方式下&#xff0c;设备最多可…

C++学习笔记—— C++内存管理方式:new和delete操作符进行动态内存管理

系列文章目录 http://t.csdnimg.cn/d0MZH 目录 系列文章目录http://t.csdnimg.cn/d0MZH 比喻和理解a.比喻C语言开空间C开空间 b.理解a、C语言的内存管理的缺点1、开发效率低&#xff08;信息传递繁琐&#xff09;2、可读性低&#xff08;信息展示混乱&#xff09;3、稳定性差&…

【MATLAB】基于VMD分解的信号去噪算法(基础版)

代码的使用说明 基于VMD分解的信号去噪算法&#xff08;基础版&#xff09; 代码流程图 代码效果图 本文代码&#xff1a;阿里云盘分享 获取代码请关注MATLAB科研小白的个人公众号&#xff08;即文章下方二维码&#xff09;&#xff0c;并回复VMD去噪 本公众号致力于解决找代…

铭飞CMS list 接口 SQL注入漏洞复现

0x01 产品简介 铭飞CMS是一款基于java开发的一套轻量级开源内容管理系统,铭飞CMS简洁、安全、开源、免费,可运行在Linux、Windows、MacOSX、Solaris等各种平台上,专注为公司企业、个人站长快速建站提供解决方案 0x02 漏洞概述 铭飞CMS在5.2.10版本以前list 接口处存在sql注入…

AI抽烟识别系统研发关键

为了设计一个有效的AI抽烟识别系统&#xff0c;我们需要考虑几个关键组成部分&#xff1a;图像捕捉、数据处理、模型训练、以及实际应用场景。下面是这个方案的详细阐述&#xff1a; 1. 图像捕捉与数据收集 摄像头部署&#xff1a;首先&#xff0c;在需要监控的区域安装高分辨…

社交网络分析1:起源发展、不同领域的应用、核心概念

社交网络分析1&#xff1a;社交网络相关定义和概念 写在最前面关于课程 社交网络、社交网络分析社交网络发展阶段&#xff08;自己感兴趣&#xff09;1. 社交网络的起源2. 社交网络的演变3. 社交网络的成熟4. 发展阶段补充和展望 2023社交大变革&#xff08;自己感兴趣的点&…

带你手把手解读firejail沙盒源码(0.9.72版本) (三) fcopy

文章目录 main.c该模块的各个函数功能详解selinux_relabel_pathcopy_filemkdir_attrcopy_linkproc_pid_to_selffs_copydircheckduplicate_dirduplicate_fileduplicate_linkmain Makefile main.c 文件总结 ├── fcopy │ ├── Makefile │ └── main.cmain.c #include…

深度学习中的高斯分布

1 高斯分布数学表达 1.1 什么是高斯分布 高斯分布(Gaussian Distribution)又称正态分布(Normal Distribution)。高斯分布是一种重要的模型&#xff0c;其广泛应用与连续型随机变量的分布中&#xff0c;在数据分析领域中高斯分布占有重要地位。高斯分布是一个非常常见的连续概…

【Spring】03 容器

文章目录 1. 定义2. BeanFactory1&#xff09;惰性加载2&#xff09;基本的容器功能3&#xff09;XML配置 3. ApplicationContext1&#xff09;主动加载2&#xff09;AOP支持3&#xff09;事件发布与监听4&#xff09;国际化支持5&#xff09;注解支持 4. Spring容器的生命周期…