Qt 之 HTTP 请求 多线程分块下载——上(获取下载文件大小)

简述

Qt 之 HTTP 请求下载(支持断点续传) 文章中我们使用Qt 的方法进行HTTP 请求下载文件,同时能够断点续传,本篇文章是这一篇文章的续篇。

我们一般在网上下载软件、电影、文件等,大都是使用迅雷下载,因为迅雷下载相对而言较快。在我们日常生活中,使用迅雷下载大文件时,非常占网速,甚至会占用整个家庭局域网的带宽。有时候浏览器都打不开网页,但是迅雷有限速功能,能够保护正常上网。下面看一张图。

这里写图片描述

这是最新的迅雷9设置界面,我们看到画圈的地方,这里有一个原始地址线程数,默认是5,范围为1-10,也就是迅雷将一个下载任务分成了5个线程分别进行下载,5个线程相对一个线程而言,资源的抢占的相对较多,下载速度相对较快。

难道说线程越多,下载速度越快吗?

不一定,因为下载速度还是看网络的最大带宽的,最大下载速度不可能超过宽带运营商提供的最大带宽(还有就是本地电脑的网卡),但是多个线程相对一个线程而言,总体的速度肯定会有所提升。就好比下面这一张图,我们可以将每个矩形看做一根管子,水从上面红色的管子流下,绿色的管子为本地电脑网卡,一般情况下网卡为百兆网卡,相对网络带宽而言,这根绿色的管子相对粗一些,所以电脑网卡一般情况下不会限制网速。再看下面棕色的管子,这里表示浏览器等一些其他软件所占用的网速,而蓝色的管子则为迅雷下载占用的网速,对比左右两个图,可以看出3根管子的水流量 >= 1根管子的水流量也就是说线程越多抢占的资源相对较多

这里写图片描述

线程是越多越好吗?

不一定,因为红色的管子的半径是固定的,即水流量是一定的,而如果不断增加下面蓝色的管子,这样每根管子的水流量就会相对减少。而创建的管子越多,必然会消耗更多的资源,所以并不是线程越多越好。线程要控制在一定的范围内,就迅雷而言,就将一个任务的线程控制在1~10范围内。


我们在使用Qt 的方法进行HTTP进行下载时,可以先对文件进行分块,然后对每一块创建一个线程进行下载,上面也讲述了多线程分块下载的好处,但是线程的数目需要自己控制好,下面我们先讲述如何用HTTP请求一个文件的总大小。

代码之路

qint64 getFileTotalSize(QString url, int tryTimes)
{
    qint64 size = -1;

    if (tryTimes <= 0)
    {
        tryTimes = 1;
    }

    do 
    {
        QNetworkAccessManager manager;
        // 事件循环,等待请求文件头信息结束;
        QEventLoop loop;
        // 超时,结束事件循环;
        QTimer timer;

        //发出请求,获取文件地址的头部信息;
        QNetworkReply *reply = manager.head(QNetworkRequest(url));
        if (!reply)
            continue;

        connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
        connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));

        timer.start(2000);
        loop.exec();

        if (reply->error() != QNetworkReply::NoError)
        {
            qDebug() << reply->errorString();
            continue;
        }
        if (reply->error() != QNetworkReply::NoError)
        {
            // 请求发生错误;
            qDebug() << reply->errorString();
            continue;
        }
        else if (!timer.isActive())
        {
            // 请求超时超时,未获取到文件信息;
            qDebug() << "Request Timeout";
            continue;
        }
        timer.stop();

        QVariant var = reply->header(QNetworkRequest::ContentLengthHeader);
        size = var.toLongLong();
        reply->deleteLater();
        break;
    } while (tryTimes--);

    return size;
}

简单分析

上述提供了一个方法来通过HTTP请求获取到下载文件的大小,因为我们要对一个文件进行分块下载,那么首先肯定是要知道文件的大小,要不然怎么分块呢。

qint64 getFileTotalSize(QString url, int tryTimes)

第一个参数为下载文件的url,第二个参数为请求的次数,因为可能某种原因会导致请求失败(比如当前网络不是很好,或者文件服务器繁忙),所以做了三次尝试,尽可能保证请求成功。

对于请求次数首先要进行判断,防止次数为负数或者零。在do-while循环中进行请求文件大小。

这里我们使用了Qt中的事件循环来等待文件大小的请求结束,在文件请求完后结束事件循环。为了防止请求过慢,事件循环时间过长,这里加上了超时,超过2s结束事件循环,重新请求。(因为请求文件头信息在一般情况下耗时较短,除非网络不好或者文件服务器出了问题。具体超时时间按具体情况而定)

我们通过使用 reply->header(QNetworkRequest::ContentLengthHeader); 来获取文件头大小。参数具体可以看下图。

这里写图片描述


通过上面的叙述,我们获取到了要下载文件的大小,代码也很简单,但这只是第一步。下篇文章中将讲述如何分块,并创建多个线程分别下载各个文件块,同时能够保存下载信息,在程序第二次打开时继续下载。

  • 4
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Qt中进行HTTP请求可以使用Qt提供的QNetworkAccessManager类。而要在多线程中进行HTTP请求,可以使用Qt的线程机制来实现。 以下是一个简单的例子,演示如何在多线程中使用QNetworkAccessManager发送HTTP请求获取JSON结果: ```cpp // MyThread.h #ifndef MYTHREAD_H #define MYTHREAD_H #include <QThread> #include <QNetworkAccessManager> #include <QNetworkReply> class MyThread : public QThread { Q_OBJECT public: MyThread(QObject *parent = nullptr); protected: void run() override; signals: void finished(const QString &result); private: QNetworkAccessManager *m_manager; }; #endif // MYTHREAD_H ``` ```cpp // MyThread.cpp #include "MyThread.h" #include <QUrl> #include <QNetworkRequest> #include <QJsonDocument> MyThread::MyThread(QObject *parent) : QThread(parent) , m_manager(new QNetworkAccessManager(this)) { } void MyThread::run() { QUrl url("http://example.com/api"); QNetworkRequest request(url); QNetworkReply *reply = m_manager->get(request); // 等待 HTTP 请求完成 QEventLoop loop; connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); // 从 JSON 字符串中解析结果 QString result; if (reply->error() == QNetworkReply::NoError) { QByteArray data = reply->readAll(); QJsonDocument doc = QJsonDocument::fromJson(data); if (!doc.isNull()) { result = doc.toJson(QJsonDocument::Compact); } } reply->deleteLater(); emit finished(result); } ``` 在主线程中创建并启动 MyThread 对象: ```cpp MyThread *thread = new MyThread(this); connect(thread, &MyThread::finished, [=](const QString &result) { // 处理结果 }); thread->start(); ``` 在 MyThread 类中,首先创建一个 QNetworkAccessManager 对象,然后使用它发送 HTTP 请求。在请求完成后,从 QNetworkReply 对象中读取返回的数据,并将其解析为 JSON 字符串。最后,将结果通过信号传递给主线程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值