Qt系列文章020-HTTP断点续传

本文介绍了如何在Qt环境下实现HTTP断点续传下载功能,强调该功能依赖于服务端的支持,并通过示例代码展示了如何在暂停和继续下载之间切换,以及如何处理下载进度、速度和剩余时间。文章提供了动态效果展示,演示了下载过程中的暂停和续传操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 前言

    上一篇文章主要讲解了Http下载的当前实时网速记录,剩余时间,下载大小,唯独没有讲解如何使用断点续传,因为断点续传有点啰嗦和麻烦,所以才单独开辟一章,首先断点续传这个功能不是Http自带的,而是需要服务端那边有相应处理才行,如果服务端没有对应处理 ,你再断也就是断了,翻了天也续不上来,所以不要认为http断点续传是天生就自带的功能,这个主要是看对应服务端是否有这一功能,做了这一处理,一般的大型下载服务端,如Nginx之类的映射下载都是带了此机制的,如果是自定义开发的服务端接口就要看是否带了,一般的约定也是用请求Http的头字段设置Range参数,下面就看具体介绍。

    下面是下载的360软件包的断点续传动态效果图,提前放出,知道是咋回事,后面动态效果会有详细说明,这里已下载大小等还有点问题,没有修改,就算是留给大家的问题吧!

公众号:Qt实战,各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发。

公众号:Qt入门和进阶,专门介绍Qt/C++相关知识点学习,帮助Qt开发者更好的深入学习Qt。多位Qt元婴期大神,一步步带你从入门到进阶,走上财务自由之路。

官方店:https://shop114595942.taobao.com//

在这里插入图片描述

2 实例讲解

    程序还是参考之前的HTTP_Demo 程序,我在其上加上两个按钮,暂停,开始,这样就能很好的诠释断点续传的功能,看下面更改的示意图:(其他相应的功能代码也有所改动,会重新介绍
在这里插入图片描述
HTTP_Demo 头文件新增了一些字段,看代码显示:

class HTTP_Demo : public QWidget
{
    Q_OBJECT

public:
    HTTP_Demo(QWidget *parent = nullptr);
    ~HTTP_Demo();

    //时间格式化输出
    QString timeFormat(qint64  seconds);
    //速度格式化输出
    QString speed(double speed);
  	//已经下载的大小格式化输出
    QString size(qint64 bytes);

    //计算下载进度各种数据,实时下载速度,已下载大小,剩余时间等,有更改
    void calculateDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);

    void stopWork();
private slots:
    //使用缺省值默认路径触发
    void on_pushButton_defaultPath_clicked();
    //选择文件夹路径触发
    void on_pushButton_selectFilePath_clicked();
    //下载按钮触发
    void on_pushButton_download_clicked();

    //下载完成后的槽
    void onFinish();
    void onReadyRead();
    void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
    void on_pushButton_pause_clicked();

    void on_pushButton_start_clicked();

private:
    Ui::HTTP_Demo *ui;

    QNetworkAccessManager *mNetworkManager = nullptr; //网络管理
    QNetworkReply *mPReply = nullptr; //网络响应
    QTime m_timeRecord; //记录下载实时网速用时
    int m_timeInterval= 0; //时间记录的间隔差距

    qint64 m_bytesReceived = 0; ///当前的接收数据大小
    qint64 m_bytesTotal = 0;  ///当前下载的数据总大小
    qint64 m_currentDownloadSize = 0; ///当前已经下载的大小
    qint64 m_intervalDownloadSize = 0; ///计算速度之前下载的大小的累计和,计算速度用

    //新增---
    bool m_isStop = true; //使用点击了暂停按钮
    qint64 m_bytesCurrentReceived  = 0;
    bool m_isSupportBreakPoint = true;
    QString m_fileName;
    QByteArray m_ReplyData;

};

    新增加了开始和暂停按钮,下面看暂停按钮的代码:

void HTTP_Demo::on_pushButton_pause_clicked()
{
    // 这里m_isStop变量为了保护多次点击暂停下载按钮,导致m_bytesCurrentReceived 被不停累加;
    if (!m_isStop)
    {
        //记录当前已经下载字节数
        m_bytesCurrentReceived += m_bytesReceived;
        stopWork();
    }
}

// 停止下载工作;
void HTTP_Demo::stopWork()
{
    m_isStop = true;
    if (mPReply != nullptr)
    {
        disconnect (mPReply, &QNetworkReply::finished,  this, &HTTP_Demo::onFinish) ;
        disconnect (mPReply, &QNetworkReply::readyRead, this, &HTTP_Demo::onReadyRead) ;
        disconnect (mPReply, &QNetworkReply::downloadProgress,  this, &HTTP_Demo::onDownloadProgress) ;
        mPReply->abort();
        mPReply->deleteLater();
        mPReply = nullptr;
    }
}

    暂停按钮主要是记录了当前下载的所有字节数,然后停止下载工作,后续继续的话就要用到此字段来进行断点续传功能了,下面看开始按钮代码,就会有用到, 开始按钮就是下载按钮的代码,两个共用的,所以主要改动也在这里:

void HTTP_Demo::on_pushButton_download_clicked()
{
    if(m_isStop)
    {
        m_isStop = false;
        //开始下载
        QString urlSpec = ui->lineEdit_downloadURL->text ().trimmed() ;
        if (urlSpec. isEmpty() )
        {
            QMessageBox::information(this, "错误","请指定需要下载的URL") ;
            m_isStop = true;
            return;
        }
        bool isHttpsRequest;
        //不区分大小写
        if(urlSpec.startsWith("https:", Qt::CaseInsensitive))
        {
            isHttpsRequest = true;
        }

        QUrl newUrl = QUrl(urlSpec) ;
        if ( !newUrl. isValid() )
        {
            QMessageBox:: information(this,"错误",QString("无效URL: 81 \n错误信息: 82") .arg (urlSpec, newUrl.errorString())) ;
            m_isStop = true;
            return;
        }
        QString tempDir =ui->lineEdit_fileSavePath->text().trimmed() ;
        if (tempDir. isEmpty())
        {
            QMessageBox::information(this, "错误", "请指定保存下载文件的目录") ;
            m_isStop = true;
            return;
        }

        QString fileName = newUrl.fileName ();

        //如果找不到文件名称,就默认写一个
        if(fileName.isEmpty())
        {
            fileName = "temp.date";
        }
        QString fullFileName =tempDir+fileName ;

		----新增改动
        // 如果当前下载的字节数为0那么说明未下载过或者重新下载
        // 则需要检测本地是否存在之前下载的临时文件,如果有则删除
        if (m_bytesCurrentReceived <= 0)
        {
            m_ReplyData.clear();
            QFile::remove(fullFileName);
        }
        else if (QFile::exists (fullFileName))
        {
            QFile::remove(fullFileName) ;
        }
        m_fileName = fullFileName;

        ui->pushButton_download->setEnabled(false) ;
        if(mNetworkManager == nullptr)
        {
            mNetworkManager = new QNetworkAccessManager(this);
        }

        QNetworkRequest request2(newUrl);
	    -----新增改动
        // 如果支持断点续传,则设置请求头信息
        if (m_isSupportBreakPoint)
        {
            qInfo()<<"进入断点续传";
            QString strRange = QString("bytes=%1-").arg(m_bytesCurrentReceived);
            request2.setRawHeader("Range", strRange.toLatin1());
        }

        mPReply = mNetworkManager->get(request2);

        connect (mPReply, &QNetworkReply::finished,  this, &HTTP_Demo::onFinish) ;
        connect (mPReply, &QNetworkReply::readyRead, this, &HTTP_Demo::onReadyRead) ;
        connect (mPReply, &QNetworkReply::downloadProgress,  this, &HTTP_Demo::onDownloadProgress) ;

        m_timeRecord.start();
    }
}

    然后就是onReadyRead代码改动,之前是写入文件,后面测试发现写入文件有差异化,可能导致失败,所以改成只记录相关数据:

void HTTP_Demo::onReadyRead()
{
    if(!m_isStop)
    {
         m_ReplyData += mPReply->readAll();
    }
}

onDownloadProgress 代码改动如下:

void HTTP_Demo::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
    m_bytesReceived = bytesReceived;
    m_bytesTotal = bytesTotal;

    //下载进程,设置最大值为100
    ui->progressBar->setMinimum(0);
    ui->progressBar->setMaximum(100) ;

    calculateDownloadProgress(m_bytesReceived+m_bytesCurrentReceived, m_bytesTotal + m_bytesCurrentReceived);
}

    其中calculateDownloadProgress方法没有大改动,只是将全局变量m_bytesReceived 改成了 局部变量,下面是改动代码:

void HTTP_Demo::calculateDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
    //计算您当前的进度
    float currentProgressValue = static_cast<float>(bytesReceived)/static_cast<float>(bytesTotal)*100;

    ui->progressBar-> setValue (currentProgressValue) ;

    // 输出当前下载进度;
    // 用到除法需要注意除0错误;
    //qDebug() << QString("%1").arg(bytesReceived * 100 / bytesTotal + 1);


    // m_intervalDownload 为下次计算速度之前的下载字节数;
    m_intervalDownloadSize += bytesReceived - m_currentDownloadSize;
    m_currentDownloadSize = bytesReceived;

    int timeNow = m_timeRecord.elapsed();

    // 超过0.3s更新计算一次速度;
    if (timeNow - m_timeInterval > TIME_INTERVAL)
    {
        qint64 ispeed = m_intervalDownloadSize * 1000 / (timeNow - m_timeInterval);
        QString strSpeed = speed(ispeed);
        ui->label_speed->setText("当前下载速度:"+strSpeed);

        // 剩余时间;
        qint64 timeRemain = 0;
        if (ispeed != 0)
        {
            timeRemain = (bytesTotal - bytesReceived) / ispeed;
        }

        //转换单位(下载剩余时间)
        QString strTimeRemain = timeFormat(timeRemain);

        ui->label_time->setText("剩余时间:"+strTimeRemain);
        //当前已下载的数据大小
        QString currentFileDownSize = size(m_currentDownloadSize);

        ui->label_downSize->setText("已下载大小:"+currentFileDownSize);

        //下载文件的总大小
        QString totalFileSize = size(bytesTotal);
        ui->label_totalSize->setText("文件总大小:"+totalFileSize);

        m_intervalDownloadSize = 0;
        m_timeInterval = timeNow;
    }
}

    最后就是onFinish了,之前的写法onFinish只是析构了mPReply,改动后,是最关键的一步,就是将数据写入文件,代码如下:

void HTTP_Demo::onFinish()
{
    mPReply->deleteLater() ;
    mPReply = nullptr;
    QFile file(m_fileName);
    if(file.open(QIODevice::WriteOnly))
    {
        file.write(m_ReplyData);
    }

    file.close();

    ui ->pushButton_download->setEnabled(true) ;
}

3 动态图显示效果

    为了呈现更好的效果,我这边还是使用360安装软件包来做测试,测试链接依然是之前的,从360官网获取的,大家可以自行测试!
https://dl.360safe.com/pclianmeng/n/1__4000943.exe?source=%E6%9C%A8%E9%A9%AC%E6%9F%A5%E6%9D%80

在这里插入图片描述
    从动态图中可以看到,下载是成功的,可以中途暂停,也可以继续下载,然后下载软件也是能打开的,刚才看了下动图发现显示已下载大小有点问题,这个问题就留给大家解决了,也不是难点, 好了,至于其他更细节的断点续传就靠各位自己去深入研究了,还是那句话,断点续传一定要看对方下载服务端支持不,不过一般的外用下载连接都是支持的,私人服务端接口可能就不一定,好了就到这里结束了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值