QT学习总结--断点下载

zhy

发布于 2020.03.31 13:01 阅读 3172 评论 0

    想要利用qt完成断点下载需要了解一下http请求的Range字段。 Range字段的作用是支持http协议的范围请求,用法如下:

   Range:bytes=1-99  表示下载1到99字节

   Range:bytes=100-   表示下载100字节之后的所有字节

  

另外,qt提供了downloadProgress信号(qint64 ,qin64)来提供下载进度的反馈,第一个参数为收到的字节数,第二个为总字节数,两者都是指的一次请求的接收字节数和总字节数(如:Range:bytes=100-199  这次请求总字节数为100)


 

效果:

 

 

    在下载链接中添加地址,点击start按钮开始或继续下载,点击stop按钮停止,点击close关闭下载,删除临时文件。在下载完成前,以临时文件的形式存在,完成后改为下载的文件格式,在暂停下载后,保存已下载的字节,作为下次下载的起点。

 

主要的功能类为DownLoadManager,主要有下载,停止,进度通知,完成功能。

利用downloadProgress,finished,readyread信号与功能关联。

 

   代码如下:

 

downloadmanager.cpp
#include "downloadmanager.h"
#include <QFile>
#include <QDebug>
#include <QFileInfo>
#include <QMessageBox>

#define DOWNLOAD_FILE_SUFFIX    "_tmp"

DownLoadManager::DownLoadManager(QObject *parent)
    : QObject(parent)

{
    m_networkManager = new QNetworkAccessManager(this);
}

DownLoadManager::~DownLoadManager()
{}

// 设置是否支持断点续传;
void DownLoadManager::setDownInto(bool isSupportBreakPoint)
{
    m_isSupportBreakPoint = isSupportBreakPoint;
}

// 获取当前下载链接;
QString DownLoadManager::getDownloadUrl()
{
    return m_url.toString();
}

// 开始下载文件,传入下载链接和文件的路径;
void DownLoadManager::downloadFile(QString url , QString fileName)
{
    // 防止多次点击开始下载按钮,进行多次下载,只有在停止标志变量为true时才进行下载;
    if (m_isStop)
    {
        m_isStop = false;
        m_url = QUrl(url);

        // 将当前文件名设置为临时文件名,下载完成时修改回来;
        m_fileName = fileName + DOWNLOAD_FILE_SUFFIX;

        // 如果当前下载的字节数为0那么说明未下载过或者重新下载
        // 则需要检测本地是否存在之前下载的临时文件,如果有则删除
        if (m_bytesCurrentReceived <= 0)
        {
            removeFile(m_fileName);
        }

        QNetworkRequest request;
        request.setUrl(m_url);

        // 如果支持断点续传,则设置请求头信息
        if (m_isSupportBreakPoint)
        {
            QString strRange = QString("bytes=%1-").arg(m_bytesCurrentReceived);
            request.setRawHeader("Range", strRange.toLatin1());
        }

        // 请求下载;
        m_reply = m_networkManager->get(request);

        connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(onDownloadProgress(qint64, qint64)));
        connect(m_reply, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
        connect(m_reply, SIGNAL(finished()), this, SLOT(onFinished()));
        connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
    }
}

// 下载进度信息;
void DownLoadManager::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
    if (!m_isStop)
    {
        m_bytesReceived = bytesReceived;
        m_bytesTotal = bytesTotal;
        // 更新下载进度;(加上 m_bytesCurrentReceived 是为了断点续传时之前下载的字节)
        emit signalDownloadProcess(m_bytesReceived + m_bytesCurrentReceived, m_bytesTotal + m_bytesCurrentReceived);
    }
}

// 获取下载内容,保存到文件中;
void DownLoadManager::onReadyRead()
{
    if (!m_isStop)
    {
        QFile file(m_fileName);
        if (file.open(QIODevice::WriteOnly | QIODevice::Append))
        {
            file.write(m_reply->readAll());
        }
        file.close();
    }
}

// 下载完成;
void DownLoadManager::onFinished()
{
    m_isStop = true;
    // http请求状态码;
    QVariant statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);

    if (m_reply->error() == QNetworkReply::NoError)
    {
        // 重命名临时文件;
        QFileInfo fileInfo(m_fileName);
        if (fileInfo.exists())
        {
            int index = m_fileName.lastIndexOf(DOWNLOAD_FILE_SUFFIX);
            QString realName = m_fileName.left(index);
            QFile::rename(m_fileName, realName);
        }
    }
    else
    {
        // 有错误输出错误;
        QString strError = m_reply->errorString();
        qDebug() << "__________" + strError;
    }

    emit signalReplyFinished(statusCode.toInt());
}

// 下载过程中出现错误,关闭下载,并上报错误,这里未上报错误类型,可自己定义进行上报;
void DownLoadManager::onError(QNetworkReply::NetworkError code)
{
    QString strError = m_reply->errorString();
    qDebug() << "__________" + strError;

    closeDownload();
    QMessageBox::information(NULL, "警告","出现错误!");
}

// 停止下载工作;
void DownLoadManager::stopWork()
{
    m_isStop = true;
    if (m_reply != NULL)
    {
        disconnect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(onDownloadProgress(qint64, qint64)));
        disconnect(m_reply, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
        disconnect(m_reply, SIGNAL(finished()), this, SLOT(onFinished()));
        disconnect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
        m_reply->abort();
        m_reply->deleteLater();
        m_reply = NULL;
    }
}

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

// 重置参数;
void DownLoadManager::reset()
{
    m_bytesCurrentReceived = 0;
    m_bytesReceived = 0;
    m_bytesTotal = 0;
}

// 删除文件;
void DownLoadManager::removeFile(QString fileName)
{
    // 删除已下载的临时文件;
    QFileInfo fileInfo(fileName);
    if (fileInfo.exists())
    {
        QFile::remove(fileName);
    }
}

// 停止下载按钮被按下,关闭下载,重置参数,并删除下载的临时文件;
void DownLoadManager::closeDownload()
{
    stopWork();
    reset();
    removeFile(m_fileName);
}

 

mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)

{
    ui->setupUi(this);
    initWindow();
}

MainWindow::~MainWindow()
{
}

void MainWindow::initWindow()
{
    ui->progressBar->setValue(0);
    connect(ui->pButtonStart, SIGNAL(clicked()), this, SLOT(onStartDownload()));
    connect(ui->pButtonStop, SIGNAL(clicked()), this, SLOT(onStopDownload()));
    connect(ui->pButtonClose, SIGNAL(clicked()), this, SLOT(onCloseDownload()));
}

// 开始下载;
void MainWindow::onStartDownload()
{
    // 从界面获取下载链接;
    m_url = ui->downloadUrl->text();
    if (m_downloadManager == NULL)
    {
        m_downloadManager = new DownLoadManager(this);
        connect(m_downloadManager , SIGNAL(signalDownloadProcess(qint64, qint64)), this, SLOT(onDownloadProcess(qint64, qint64)));
        connect(m_downloadManager, SIGNAL(signalReplyFinished(int)), this, SLOT(onReplyFinished(int)));
    }

    // 这里先获取到m_downloadManager中的url与当前的m_url 对比,如果url变了需要重置参数,防止文件下载不全;
    QString url = m_downloadManager->getDownloadUrl();
    if (url != m_url)
    {
        m_downloadManager->reset();
    }
    m_downloadManager->setDownInto(true);

    QString savePath = "H:/test/";
        savePath += m_url.mid(m_url.lastIndexOf("/")+1, (m_url.lastIndexOf("?")>0 ? m_url.lastIndexOf("?") : m_url.length()) - m_url.lastIndexOf("/"));


    m_downloadManager->downloadFile(m_url, savePath);
    m_timeRecord.start();
    m_timeInterval = 0;
    ui->labelStatus->setText(QStringLiteral("正在下载"));
}

// 暂停下载;
void MainWindow::onStopDownload()
{
    ui->labelStatus->setText(QStringLiteral("停止下载"));
    if (m_downloadManager != NULL)
    {
        m_downloadManager->stopDownload();
    }
}

// 关闭下载;
void MainWindow::onCloseDownload()
{
    m_downloadManager->closeDownload();
    ui->progressBar->setValue(0);
    ui->labelStatus->setText(QStringLiteral("关闭下载"));
}

// 更新下载进度条;
void MainWindow::onDownloadProcess(qint64 bytesReceived, qint64 bytesTotal)
{
    // 输出当前下载进度;
    qDebug() << QString("%1").arg(bytesReceived * 100 / (bytesTotal+1));
    // 更新进度条;
    ui->progressBar->setMaximum(bytesTotal);
    ui->progressBar->setValue(bytesReceived);

}

// 下载完成;
void MainWindow::onReplyFinished(int statusCode)
{
    // 根据状态码判断当前下载是否出错;
    if (statusCode >= 200 && statusCode < 400)
    {
        qDebug() << "Download Success";
    }
    else
    {
        qDebug() << "Download Failed";
    }
}