diff --git a/src/downloadworker.cpp b/src/downloadworker.cpp
new file mode 100644
index 0000000..8ca0a13
--- /dev/null
+++ b/src/downloadworker.cpp
@@ -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;
+}
+
diff --git a/src/downloadworker.h b/src/downloadworker.h
new file mode 100644
index 0000000..8e719d6
--- /dev/null
+++ b/src/downloadworker.h
@@ -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
diff --git a/src/spark-store.pro b/src/spark-store.pro
index 09c2bbe..14407ec 100644
--- a/src/spark-store.pro
+++ b/src/spark-store.pro
@@ -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 \
diff --git a/src/widget.cpp b/src/widget.cpp
index 796dacf..d7f3f89 100644
--- a/src/widget.cpp
+++ b/src/widget.cpp
@@ -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([=](){
diff --git a/src/widget.h b/src/widget.h
index a1a1bf7..80f3e23 100644
--- a/src/widget.h
+++ b/src/widget.h
@@ -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