完成并发请求下载

This commit is contained in:
metanoia1989 2021-02-16 23:00:27 +08:00
parent 1a4b1176fb
commit 2f8c11a30b
5 changed files with 355 additions and 27 deletions

234
src/downloadworker.cpp Normal file

@ -0,0 +1,234 @@
#include "downloadworker.h"
#include <QIODevice>
#include <QEventLoop>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QDebug>
#include <QThread>
DownloadWorker::DownloadWorker(QObject *parent)
{
}
void DownloadWorker::setIdentifier(int identifier)
{
this->identifier = identifier;
}
void DownloadWorker::setParamter(const QString &url, QPair<qint64, qint64> range, QFile *file)
{
this->url = url;
this->startPos = range.first;
this->endPos = range.second;
this->file = file;
}
qint64 DownloadWorker::getReceivedPos()
{
return receivedPos;
}
void DownloadWorker::doWork()
{
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());
reply = mgr->get(request);
qDebug() << "开始下载数据:" << QString(" %1~%2 -> writePos Start %3")
.arg(startPos).arg(endPos).arg(receivedPos);
connect(reply, static_cast<void(QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),
[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->disconnect();
reply->aboutToClose();
reply->deleteLater();
reply = nullptr;
}
void DownloadWorker::dataReady()
{
QByteArray data = reply->readAll();
file->seek(startPos + receivedPos);
file->write(data);
receivedPos += data.size();
}
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 = QThread::idealThreadCount() > 4 ? 4 : QThread::idealThreadCount();
}
DownloadController::~DownloadController()
{
for(int i = 0; i < workers.size(); i++) {
workers.at(i)->doStop();
workers.at(i)->disconnect();
workers.at(i)->deleteLater();
}
workers.clear();
}
void DownloadController::setFilename(QString filename)
{
this->filename = filename;
}
void DownloadController::setThreadNum(int threadNum)
{
this->threadNum = threadNum;
}
/**
* @brief
*/
void DownloadController::startDownload(const QString &url)
{
// 下载任务等分,计算每个线程的下载数据
fileSize = getFileSize(url);
if (fileSize == 0) {
emit errorOccur("文件大小获取失败");
return;
}
qint64 segmentSize = fileSize / threadNum;
ranges.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;
receivedBytes[i] = 0;
}
ranges[threadNum-1].second = fileSize; // 余数部分加入最后一个
// 打开文件
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);
// 创建下载线程
workers.clear();
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);
auto worker = new DownloadWorker(this);
auto range = ranges.at(i);
worker->setIdentifier(i);
worker->setParamter(url, range, file);
workers.append(worker);
connect(worker, &DownloadWorker::downloadProcess, this, &DownloadController::handleProcess);
connect(worker, &DownloadWorker::workFinished, this, &DownloadController::chunkDownloadFinish);
worker->doWork();
}
}
/**
* @brief
*/
void DownloadController::stopDownload()
{
for(int i = 0; i < workers.size(); i++) {
workers.at(i)->doStop();
workers.at(i)->disconnect();
workers.at(i)->deleteLater();
}
workers.clear();
// file->flush();
file->close();
delete file;
file = nullptr;
}
void DownloadController::handleProcess()
{
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) {
file->flush();
file->close();
delete file;
file = nullptr;
for(int i = 0; i < workers.size(); i++) {
workers.at(i)->doStop();
workers.at(i)->disconnect();
workers.at(i)->deleteLater();
}
workers.clear();
emit downloadFinished();
}
}
qint64 DownloadController::getFileSize(const QString& url)
{
QEventLoop event;
QNetworkAccessManager requestManager;
QNetworkRequest request;
request.setUrl(QUrl(url));
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
QNetworkReply *reply = requestManager.head(request);
connect(reply, static_cast<void(QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),
[this, reply](QNetworkReply::NetworkError error){
if (error != QNetworkReply::NoError) {
emit errorOccur(reply->errorString());
}
});
connect(reply, &QNetworkReply::finished, &event, &QEventLoop::quit);
event.exec();
qint64 fileSize = 0;
if (reply->rawHeader("Accept-Ranges") == QByteArrayLiteral("bytes")
&& reply->hasRawHeader(QString("Content-Length").toLocal8Bit())) {
fileSize = reply->header(QNetworkRequest::ContentLengthHeader).toUInt();
}
qDebug() << "文件大小为:" << fileSize;
reply->deleteLater();
return fileSize;
}

73
src/downloadworker.h Normal file

@ -0,0 +1,73 @@
#ifndef DOWNLOADWORKER_H
#define DOWNLOADWORKER_H
#include <QObject>
#include <QList>
#include <QFile>
#include <QNetworkReply>
class DownloadWorker : public QObject
{
Q_OBJECT
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;
qint64 startPos;
qint64 endPos;
qint64 receivedPos = 0;
QNetworkReply *reply;
QNetworkAccessManager *mgr;
QFile *file;
};
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);
void stopDownload();
qint64 getFileSize(const QString& url);
public slots:
void handleProcess();
void chunkDownloadFinish();
signals:
void errorOccur(const QString& msg);
void downloadProcess(qint64, qint64);
void downloadFinished();
private:
int threadNum;
QString filename;
qint64 fileSize;
QVector<QPair<qint64, qint64>> ranges;
QFile *file;
QList<DownloadWorker*> workers;
int finish = 0;
};
#endif // FILEDOWNLOADWORKER_H

@ -27,6 +27,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += main.cpp\
appitem.cpp \
downloadworker.cpp \
widget.cpp \
downloadlist.cpp \
image_show.cpp \
@ -37,6 +38,7 @@ SOURCES += main.cpp\
HEADERS += \
appitem.h \
downloadworker.h \
widget.h \
downloadlist.h \
image_show.h \

@ -33,6 +33,7 @@
#include "HttpClient.h"
#include "appitem.h"
#include "flowlayout.h"
#include "downloadworker.h"
DWIDGET_USE_NAMESPACE
@ -48,6 +49,8 @@ Widget::Widget(DBlurEffectWidget *parent) :
m_loadweb->show();
httpClient = new AeaQt::HttpClient;
// 并发下载
downloadController = new DownloadController(this);
connect(ui->menu_main,&QPushButton::clicked,[=](){Widget::chooseLeftMenu(0);});
connect(ui->menu_network,&QPushButton::clicked,[=](){Widget::chooseLeftMenu(1);});
@ -72,6 +75,7 @@ Widget::Widget(DBlurEffectWidget *parent) :
connect(&appinfoLoadThread, &SpkAppInfoLoaderThread::finishedScreenshotLoad, this, &Widget::sltAppinfoScreenshot, Qt::ConnectionType::BlockingQueuedConnection);
connect(&appinfoLoadThread, &SpkAppInfoLoaderThread::finishAllLoading, this, &Widget::sltAppinfoFinish, Qt::ConnectionType::BlockingQueuedConnection);
// 搜索事件
connect(searchEdit, &DSearchEdit::returnPressed, this, [=]()
{
@ -705,31 +709,41 @@ void Widget::on_pushButton_download_clicked()
system("cp icon.png icon_"+QString::number(allDownload-1).toUtf8()+".png");
download_list[allDownload-1].seticon(icon);
if(!isBusy){
file = new QFile(fileName);
if(!file->open(QIODevice::WriteOnly)){
delete file;
file = nullptr;
return ;
}
// file = new QFile(fileName);
// if(!file->open(QIODevice::WriteOnly)){
// delete file;
// file = nullptr;
// return ;
// }
nowDownload+=1;
startRequest(urList.at(nowDownload-1)); // 进行链接请求
startRequest(urList.at(nowDownload-1), fileName); // 进行链接请求
}
if(ui->pushButton_download->text()==tr("Reinstall")){
download_list[allDownload-1].reinstall=true;
}
}
void Widget::startRequest(QUrl url)
void Widget::startRequest(QUrl url, QString fileName)
{
ui->listWidget->show();
ui->label->hide();
isBusy=true;
isdownload=true;
download_list[allDownload-1].free=false;
reply = manager->get(QNetworkRequest(url));
connect(reply,SIGNAL(finished()),this,SLOT(httpFinished()));
connect(reply,SIGNAL(readyRead()),this,SLOT(httpReadyRead()));
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(updateDataReadProgress(qint64,qint64)));
// reply = manager->get(QNetworkRequest(url));
// connect(reply,SIGNAL(finished()),this,SLOT(httpFinished()));
// connect(reply,SIGNAL(readyRead()),this,SLOT(httpReadyRead()));
// connect(reply,SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(updateDataReadProgress(qint64,qint64)));
connect(downloadController, &DownloadController::downloadProcess, this, &Widget::updateDataReadProgress);
connect(downloadController, &DownloadController::downloadFinished, this, &Widget::httpFinished);
connect(downloadController, &DownloadController::errorOccur, [this](QString msg){
this->sendNotification(msg);
});
downloadController->setFilename(fileName);
downloadController->startDownload(url.toString());
}
void Widget::searchApp(QString text)
@ -846,6 +860,8 @@ void Widget::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes)
download_list[nowDownload-1].setValue((bytesRead*10000)/totalBytes); // 当前值
download_size=bytesRead;
if(download_list[nowDownload-1].close){ // 随时检测下载是否被取消
downloadController->disconnect();
downloadController->stopDownload();
download_list[nowDownload-1].closeDownload();
httpFinished();
}
@ -958,12 +974,12 @@ void Widget::sltAppinfoFinish()
void Widget::httpFinished() // 完成下载
{
file->flush();
file->close();
reply->deleteLater();
reply = nullptr;
delete file;
file = nullptr;
// file->flush();
// file->close();
// reply->deleteLater();
// reply = nullptr;
// delete file;
// file = nullptr;
isdownload=false;
isBusy=false;
download_list[nowDownload-1].readyInstall();
@ -974,14 +990,14 @@ void Widget::httpFinished() // 完成下载
nowDownload+=1;
}
QString fileName=download_list[nowDownload-1].getName();
file = new QFile(fileName);
if(!file->open(QIODevice::WriteOnly))
{
delete file;
file = nullptr;
return ;
}
startRequest(urList.at(nowDownload-1));
// file = new QFile(fileName);
// if(!file->open(QIODevice::WriteOnly))
// {
// delete file;
// file = nullptr;
// return ;
// }
startRequest(urList.at(nowDownload-1), fileName);
}
}
@ -1269,6 +1285,7 @@ void Widget::on_webEngineView_urlChanged(const QUrl &arg1)
ui->pushButton_download->setEnabled(false);
ui->stackedWidget->setCurrentIndex(2);
qDebug()<<"https://demo-one-vert.vercel.app/"+type_name+"/"+pname;
qDebug()<< "链接地址:" << arg1;
/*
load.cancel();//打开并发加载线程前关闭正在执行的线程
load = QtConcurrent::run([=](){

@ -39,6 +39,7 @@ class Widget;
class FlowLayout;
class DownloadController;
namespace AeaQt {
class HttpClient;
@ -51,7 +52,7 @@ class Widget : public DBlurEffectWidget
public:
explicit Widget(DBlurEffectWidget *parent = nullptr);
~Widget();
void startRequest(QUrl url);
void startRequest(QUrl url, QString fileName);
void searchApp(QString);
int nowDownload=0;
int allDownload=0;
@ -163,6 +164,7 @@ private:
AeaQt::HttpClient *httpClient;
FlowLayout *applist_grid;
QHBoxLayout *main;
DownloadController *downloadController;
};
#endif // WIDGET_H