完成并发下载的进度统计
This commit is contained in:
		
							parent
							
								
									2d3d35f951
								
							
						
					
					
						commit
						54fa6ff3b6
					
				@ -43,3 +43,52 @@ http://localhost:8080/
 | 
			
		||||
* https://sucdn.jerrywang.top/
 | 
			
		||||
* https://store.deepinos.org.cn/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
原来的单线程相关代码
 | 
			
		||||
```cpp
 | 
			
		||||
// 在处理下载进度的代码中,会一直检测下载是否取消
 | 
			
		||||
void Widget::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes)
 | 
			
		||||
{
 | 
			
		||||
    if(totalBytes <= 0) return;
 | 
			
		||||
    download_list[nowDownload-1].setMax(10000); // 最大值
 | 
			
		||||
    download_list[nowDownload-1].setValue((bytesRead*10000)/totalBytes);    // 当前值
 | 
			
		||||
    download_size=bytesRead;
 | 
			
		||||
    if(download_list[nowDownload-1].close){ // 随时检测下载是否被取消
 | 
			
		||||
        download_list[nowDownload-1].closeDownload();
 | 
			
		||||
        httpFinished();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
// 但是这里的 download_list[nowDownload-1].close 是在哪里修改的呢?
 | 
			
		||||
downloadlist download_list[LIST_MAX];
 | 
			
		||||
download_list 是一个下载进度组件,维持动画的,还不错。  
 | 
			
		||||
 | 
			
		||||
// 星火商店记录下载进度的代码
 | 
			
		||||
void Widget::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes)
 | 
			
		||||
{
 | 
			
		||||
    if(totalBytes <= 0) return;
 | 
			
		||||
    download_list[nowDownload-1].setMax(10000); // 最大值
 | 
			
		||||
    download_list[nowDownload-1].setValue((bytesRead*10000)/totalBytes);    // 当前值
 | 
			
		||||
    download_size=bytesRead;
 | 
			
		||||
    if(download_list[nowDownload-1].close){ // 随时检测下载是否被取消
 | 
			
		||||
        download_list[nowDownload-1].closeDownload();
 | 
			
		||||
        httpFinished();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Widget::httpFinished() // 完成下载
 | 
			
		||||
{
 | 
			
		||||
    file->flush();
 | 
			
		||||
    file->close();
 | 
			
		||||
    reply->deleteLater();
 | 
			
		||||
    reply = nullptr;
 | 
			
		||||
    delete file;
 | 
			
		||||
    file = nullptr;
 | 
			
		||||
    isdownload=false;
 | 
			
		||||
    isBusy=false;
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
然后那个多线程下载组件需要的几个功能点:
 | 
			
		||||
* 下载进度,以及下载速度记录
 | 
			
		||||
* 下载取消功能
 | 
			
		||||
* 读取设置,允许设置单线程下载 这个可以放到后面做,优先实现上面两个
 | 
			
		||||
@ -5,6 +5,7 @@
 | 
			
		||||
#include <QNetworkRequest>
 | 
			
		||||
#include <QNetworkReply>
 | 
			
		||||
#include <QDebug>
 | 
			
		||||
#include <QThread>
 | 
			
		||||
 | 
			
		||||
DownloadWorker::DownloadWorker(QObject *parent)
 | 
			
		||||
{
 | 
			
		||||
@ -19,59 +20,55 @@ void DownloadWorker::setIdentifier(int identifier)
 | 
			
		||||
void DownloadWorker::setParamter(const QString &url, QPair<qint64, qint64> range, QFile *file)
 | 
			
		||||
{
 | 
			
		||||
    this->url = url;
 | 
			
		||||
//    this->range = range;
 | 
			
		||||
    this->startPos = range.first;
 | 
			
		||||
    this->endPos = range.second;
 | 
			
		||||
    this->file = file;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
qint64 DownloadWorker::getReceivedPos()
 | 
			
		||||
{
 | 
			
		||||
    return receivedPos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DownloadWorker::doWork()
 | 
			
		||||
{
 | 
			
		||||
        QNetworkAccessManager *mgr = new QNetworkAccessManager(this);
 | 
			
		||||
        mgr = new QNetworkAccessManager(this);
 | 
			
		||||
        QNetworkRequest request;
 | 
			
		||||
        request.setUrl(url);
 | 
			
		||||
        request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
 | 
			
		||||
        request.setRawHeader("Range", QString("bytes=%1-%2").arg(startPos)
 | 
			
		||||
                    .arg(endPos).toLocal8Bit());
 | 
			
		||||
//        QNetworkReply *reply = mgr->get(request);
 | 
			
		||||
//        QNetworkReply *reply = mgr->get(request);
 | 
			
		||||
        reply = mgr->get(request);
 | 
			
		||||
//        qDebug() << "开始下载数据:" << QString(" %1~%2 -> writePos Start %3")
 | 
			
		||||
//            .arg(range.first).arg(range.second).arg(writePos);
 | 
			
		||||
        qDebug() << "开始下载数据:" << QString(" %1~%2 -> writePos Start %3")
 | 
			
		||||
            .arg(startPos).arg(endPos).arg(receivedPos);
 | 
			
		||||
//        connect(reply, &QNetworkReply::readyRead, [reply, this](){
 | 
			
		||||
////            qDebug() << "测试是否触发,午安啦啦啦";
 | 
			
		||||
//            QByteArray data = reply->readAll();
 | 
			
		||||
//            qDebug() << "获取到了数据" << " " << data.size();
 | 
			
		||||
//            emit resultReady(identifier, data);
 | 
			
		||||
//        });
 | 
			
		||||
//        connect(reply, &QNetworkReply::errorOccurred, [reply](QNetworkReply::NetworkError error){
 | 
			
		||||
//            if (error != QNetworkReply::NoError) {
 | 
			
		||||
//                qDebug() << "出错了:" << reply->errorString();
 | 
			
		||||
//            }
 | 
			
		||||
//        });
 | 
			
		||||
        connect(reply, &QNetworkReply::errorOccurred, [this](QNetworkReply::NetworkError error){
 | 
			
		||||
            if (error != QNetworkReply::NoError) {
 | 
			
		||||
                qDebug() << "出错了:" << reply->errorString();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        connect(reply, &QNetworkReply::finished, mgr, &QNetworkAccessManager::deleteLater);
 | 
			
		||||
        connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
 | 
			
		||||
        connect(reply, &QNetworkReply::readyRead, this, &DownloadWorker::dataReady);
 | 
			
		||||
        connect(reply, &QNetworkReply::finished, this, &DownloadWorker::slotFinish);
 | 
			
		||||
        connect(reply, &QNetworkReply::downloadProgress, this, &DownloadWorker::handleProcess);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DownloadWorker::doStop()
 | 
			
		||||
{
 | 
			
		||||
    reply->deleteLater();
 | 
			
		||||
    reply = nullptr;
 | 
			
		||||
    mgr->deleteLater();
 | 
			
		||||
    mgr = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DownloadWorker::dataReady()
 | 
			
		||||
{
 | 
			
		||||
            qDebug() << QString("测试是否执行: %1 %2").arg(startPos).arg(receivedPos);
 | 
			
		||||
            QByteArray data = reply->readAll();
 | 
			
		||||
            qDebug() << "获取到了数据" << " " << data.size();
 | 
			
		||||
//            emit resultReady(identifier, data);
 | 
			
		||||
//            auto controller = qobject_cast<DownloadController*>(parent());
 | 
			
		||||
//            QMetaObject::invokeMethod(controller, "handleResults", Qt::DirectConnection,
 | 
			
		||||
//                                      Q_ARG(int, identifier), Q_ARG(QByteArray, data));
 | 
			
		||||
//            QMetaObject::invokeMethod(controller, "handleResults", Qt::QueuedConnection,
 | 
			
		||||
        file->seek(startPos + receivedPos);
 | 
			
		||||
        file->write(data);
 | 
			
		||||
        qDebug() << QString("%1, %2, %3").arg(startPos).arg(receivedPos).arg(data.size());
 | 
			
		||||
        receivedPos += data.size();
 | 
			
		||||
        //                                      Q_ARG(int, identifier), Q_ARG(QByteArray, data));
 | 
			
		||||
    qDebug() << QString("测试是否执行: %1 %2").arg(startPos).arg(receivedPos);
 | 
			
		||||
    QByteArray data = reply->readAll();
 | 
			
		||||
    file->seek(startPos + receivedPos);
 | 
			
		||||
    file->write(data);
 | 
			
		||||
    receivedPos += data.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DownloadWorker::slotFinish()
 | 
			
		||||
@ -79,23 +76,20 @@ void DownloadWorker::slotFinish()
 | 
			
		||||
    file->flush();
 | 
			
		||||
    qDebug() << "数据块下载完毕:" << QString(" %1~%2 -> writePos Start %3")
 | 
			
		||||
        .arg(startPos).arg(endPos).arg(receivedPos);
 | 
			
		||||
    emit workFinished();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DownloadWorker::handleProcess(qint64, qint64)
 | 
			
		||||
{
 | 
			
		||||
    emit this->downloadProcess();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DownloadController::DownloadController(QObject *parent)
 | 
			
		||||
{
 | 
			
		||||
    this->threadNum = 4;
 | 
			
		||||
 | 
			
		||||
    // 注册信号槽传递的数据类型
 | 
			
		||||
    qRegisterMetaType<QPair<qint64, qint64>>("QPair<qint64, qint64>");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DownloadController::~DownloadController()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void DownloadController::setFilename(QString filename)
 | 
			
		||||
{
 | 
			
		||||
    this->filename = filename;
 | 
			
		||||
@ -111,79 +105,79 @@ void DownloadController::setThreadNum(int threadNum)
 | 
			
		||||
 */
 | 
			
		||||
void DownloadController::startDownload(const QString &url, qint64 fileSize)
 | 
			
		||||
{
 | 
			
		||||
    finish = 0;
 | 
			
		||||
 | 
			
		||||
    // 下载任务等分,计算每个线程的下载数据
 | 
			
		||||
    this->fileSize = fileSize;
 | 
			
		||||
    qint64 segmentSize = fileSize / threadNum;
 | 
			
		||||
    ranges.resize(threadNum);
 | 
			
		||||
//    QVector<qint64> writePosList;
 | 
			
		||||
    writePosList.resize(threadNum);
 | 
			
		||||
    QVector<qint64> receivedBytes;
 | 
			
		||||
    receivedBytes.resize(threadNum);
 | 
			
		||||
    for (int i = 0; i < threadNum; i++) {
 | 
			
		||||
        ranges[i].first = i * segmentSize;
 | 
			
		||||
        ranges[i].second = i * segmentSize + segmentSize - 1;
 | 
			
		||||
        writePosList[i]  = ranges[i].first;
 | 
			
		||||
        receivedBytes[i] = 0;
 | 
			
		||||
    }
 | 
			
		||||
    ranges[threadNum-1].second = fileSize; // 余数部分加入最后一个
 | 
			
		||||
 | 
			
		||||
    qDebug() << QString("查看分段下载:%1 %2").arg(ranges.size()).arg(writePosList.size());
 | 
			
		||||
//    return;
 | 
			
		||||
    // 打开文件
 | 
			
		||||
    file = new QFile;
 | 
			
		||||
    file->setFileName(filename);
 | 
			
		||||
    if (file->exists())
 | 
			
		||||
        file->remove();
 | 
			
		||||
    if (!file->open(QIODevice::WriteOnly)) {
 | 
			
		||||
        delete file;
 | 
			
		||||
        file = nullptr;
 | 
			
		||||
        emit errorOccur(file->errorString());
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    file->resize(fileSize);
 | 
			
		||||
 | 
			
		||||
    // 创建下载线程
 | 
			
		||||
//    qint64 bytesReceived = 0;
 | 
			
		||||
    for(int i = 0; i < ranges.size(); i++) {
 | 
			
		||||
        qDebug() << QString("第%1个线程:%2-%3").arg(i).arg(ranges.at(i).first).arg(ranges.at(i).second);
 | 
			
		||||
        qDebug() << QString("第%1个下载请求:%2-%3").arg(i).arg(ranges.at(i).first).arg(ranges.at(i).second);
 | 
			
		||||
        auto worker = new DownloadWorker(this);
 | 
			
		||||
        auto range = ranges.at(i);
 | 
			
		||||
        worker->setIdentifier(i);
 | 
			
		||||
        worker->setParamter(url, range, file);
 | 
			
		||||
//        connect(worker, &DownloadWorker::resultReady, this, &DownloadController::handleResults);
 | 
			
		||||
 | 
			
		||||
        workers.append(worker);
 | 
			
		||||
        connect(worker, &DownloadWorker::downloadProcess, this, &DownloadController::handleProcess);
 | 
			
		||||
        connect(worker, &DownloadWorker::workFinished, this, &DownloadController::chunkDownloadFinish);
 | 
			
		||||
        worker->doWork();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief 暂停下载
 | 
			
		||||
 */
 | 
			
		||||
void DownloadController::paruseDownload()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief 停止下载
 | 
			
		||||
 */
 | 
			
		||||
void DownloadController::stopDownload()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    for(int i = 0; i < workers.size(); i++) {
 | 
			
		||||
        workers.at(i)->doStop();
 | 
			
		||||
    }
 | 
			
		||||
    file->flush();
 | 
			
		||||
    file->close();
 | 
			
		||||
    delete file;
 | 
			
		||||
    file = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief 写入响应数据
 | 
			
		||||
 * @param identifier 线程编号
 | 
			
		||||
 * @param data 请求数据
 | 
			
		||||
 */
 | 
			
		||||
void DownloadController::handleResults(int identifier, QByteArray data)
 | 
			
		||||
 | 
			
		||||
void DownloadController::handleProcess()
 | 
			
		||||
{
 | 
			
		||||
    qDebug() << QString("测试哈哈哈: %1 --- %2").arg(writePosList.size()).arg(identifier);
 | 
			
		||||
    QMutexLocker lock(&mutex);
 | 
			
		||||
    qint64 writePos = writePosList.at(identifier);
 | 
			
		||||
    file->seek(writePos);
 | 
			
		||||
    file->write(data);
 | 
			
		||||
    qDebug() << QString("%1, %2, %3").arg(writePos).arg(bytesReceived).arg(data.size());
 | 
			
		||||
    writePos += data.size();
 | 
			
		||||
    writePosList.replace(identifier, writePos);
 | 
			
		||||
    bytesReceived += data.size();
 | 
			
		||||
    qDebug() << "已经下载了数据: " << bytesReceived;
 | 
			
		||||
    emit receivedProcess(bytesReceived);
 | 
			
		||||
    qint64 bytesReceived = 0;
 | 
			
		||||
    for(int i = 0; i < workers.size(); i++) {
 | 
			
		||||
        bytesReceived += workers.at(i)->getReceivedPos();
 | 
			
		||||
    }
 | 
			
		||||
    qDebug() << QString("下载进度 %1-%2").arg(bytesReceived).arg(fileSize);
 | 
			
		||||
    emit downloadProcess(bytesReceived, fileSize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DownloadController::chunkDownloadFinish()
 | 
			
		||||
{
 | 
			
		||||
    finish++;
 | 
			
		||||
    if (finish == threadNum) {
 | 
			
		||||
        emit downloadFinished();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@
 | 
			
		||||
#include <QList>
 | 
			
		||||
#include <QFile>
 | 
			
		||||
#include <QNetworkReply>
 | 
			
		||||
#include <QMutex>
 | 
			
		||||
 | 
			
		||||
class DownloadWorker : public QObject
 | 
			
		||||
{
 | 
			
		||||
@ -14,25 +13,29 @@ public:
 | 
			
		||||
    explicit DownloadWorker(QObject *parent = nullptr);
 | 
			
		||||
    void setIdentifier(int identifier);
 | 
			
		||||
    void setParamter(const QString &url, QPair<qint64, qint64> range, QFile *flle);
 | 
			
		||||
    qint64 getReceivedPos();
 | 
			
		||||
 | 
			
		||||
public slots:
 | 
			
		||||
    void doWork();
 | 
			
		||||
    void doStop();
 | 
			
		||||
    void dataReady();
 | 
			
		||||
    void slotFinish();
 | 
			
		||||
    void handleProcess(qint64, qint64);
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
    void resultReady(int identifier, QByteArray data);
 | 
			
		||||
    void testSignals();
 | 
			
		||||
 | 
			
		||||
    void workFinished();
 | 
			
		||||
    void downloadProcess();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    int identifier;
 | 
			
		||||
    QString url;
 | 
			
		||||
//    QPair<qint64, qint64> range;
 | 
			
		||||
    qint64 startPos;
 | 
			
		||||
    qint64 endPos;
 | 
			
		||||
    qint64 receivedPos = 0;
 | 
			
		||||
    QNetworkReply *reply;
 | 
			
		||||
    QNetworkAccessManager *mgr;
 | 
			
		||||
    QFile *file;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -41,30 +44,28 @@ class DownloadController : public QObject
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
public:
 | 
			
		||||
    explicit DownloadController(QObject *parent = nullptr);
 | 
			
		||||
    ~DownloadController();
 | 
			
		||||
    void setFilename(QString filename);
 | 
			
		||||
    void setThreadNum(int threadNum);
 | 
			
		||||
    void startDownload(const QString &url, qint64 fileSize);
 | 
			
		||||
    void paruseDownload();
 | 
			
		||||
    void stopDownload();
 | 
			
		||||
 | 
			
		||||
public slots:    
 | 
			
		||||
    void handleResults(int identifier, QByteArray data);
 | 
			
		||||
//    void handleTest();
 | 
			
		||||
    void handleProcess();
 | 
			
		||||
    void chunkDownloadFinish();
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
    void operate(const QString &url, const QPair<qint64, qint64> &downloadRange);
 | 
			
		||||
    void errorOccur(const QString& msg);
 | 
			
		||||
    void receivedProcess(qint64 number);
 | 
			
		||||
    void downloadProcess(qint64, qint64);
 | 
			
		||||
    void downloadFinished();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    int threadNum;
 | 
			
		||||
    QString filename;
 | 
			
		||||
    qint64 fileSize;
 | 
			
		||||
    QVector<QPair<qint64, qint64>> ranges;
 | 
			
		||||
    QFile *file;
 | 
			
		||||
    qint64 bytesReceived;
 | 
			
		||||
    QVector<qint64> writePosList;
 | 
			
		||||
    QMutex mutex;
 | 
			
		||||
    QList<DownloadWorker*> workers;
 | 
			
		||||
    int finish = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // FILEDOWNLOADWORKER_H
 | 
			
		||||
 | 
			
		||||
@ -255,11 +255,8 @@ void Widget::multiDownloadWithQtConcurrent(const QString &url, qint64 fileSize,
 | 
			
		||||
 | 
			
		||||
void Widget::multiDownloadWithQThread(const QString& url, qint64 fileSize, const QString& filename, int threadCount)
 | 
			
		||||
{
 | 
			
		||||
    DownloadController download;
 | 
			
		||||
    download.setFilename(filename);
 | 
			
		||||
    download.startDownload(url, fileSize);
 | 
			
		||||
    connect(&download, &DownloadController::receivedProcess, [fileSize, this](int receivedNum){
 | 
			
		||||
        int percent = receivedNum / fileSize;
 | 
			
		||||
        ui->downProgressBar->setValue(percent);
 | 
			
		||||
    });
 | 
			
		||||
    DownloadController *download = new DownloadController(this);
 | 
			
		||||
    download->setFilename(filename);
 | 
			
		||||
    download->startDownload(url, fileSize);
 | 
			
		||||
    connect(download, &DownloadController::downloadProcess, this, &Widget::download_progress_change);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user