From 3535f3ee035dfc2ba08bf1e590fee579f1a8a40f Mon Sep 17 00:00:00 2001 From: wuyize Date: Wed, 5 Jul 2023 23:50:11 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=B8=8A=E4=BC=A0=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AicsKnowledgeBase/qml/component/FileList.qml | 6 +- AicsKnowledgeBase/qml/page/FilePage.qml | 1 - .../src/FileTransferListModel.cpp | 11 +- AicsKnowledgeBase/src/FileTransferListModel.h | 4 +- AicsKnowledgeBase/src/FileTransferManager.cpp | 360 ++++++++++-------- AicsKnowledgeBase/src/FileTransferManager.h | 3 +- 6 files changed, 217 insertions(+), 168 deletions(-) diff --git a/AicsKnowledgeBase/qml/component/FileList.qml b/AicsKnowledgeBase/qml/component/FileList.qml index ec72db6..e992e3e 100644 --- a/AicsKnowledgeBase/qml/component/FileList.qml +++ b/AicsKnowledgeBase/qml/component/FileList.qml @@ -4,6 +4,7 @@ import FluentUI import QtQuick.Dialogs import "qrc:///AicsKnowledgeBase/qml/global" import SignalFileOperation 1.0 +import AicsKB.FileTransferManager Item { anchors.fill: parent @@ -53,13 +54,14 @@ Item { FileDialog { id: fileDialog onAccepted: function () { + let name = FileTransferManager.getFileName(selectedFile) const size = FileTransferManager.getFileSize( selectedFile) const md5 = FileTransferManager.getFileMd5(selectedFile) if (size <= 0 || md5 === '') return var body = { - "name": "test2", + "name": name, "brief": "brief", "size": size, "md5": md5, @@ -75,7 +77,7 @@ Item { console.log(data) FileTransferManager.upload( selectedFile, data.id, - data.ticket) + data.ticket, name) }, function (res, data) { console.log(res) }) diff --git a/AicsKnowledgeBase/qml/page/FilePage.qml b/AicsKnowledgeBase/qml/page/FilePage.qml index c43a418..57675d7 100644 --- a/AicsKnowledgeBase/qml/page/FilePage.qml +++ b/AicsKnowledgeBase/qml/page/FilePage.qml @@ -5,7 +5,6 @@ import QtQuick.Controls import QtQuick.Controls.Basic import QtQuick.Dialogs import FluentUI -import AicsKB.FileTransferManager import "qrc:///AicsKnowledgeBase/qml/component" import "qrc:///AicsKnowledgeBase/qml/global" diff --git a/AicsKnowledgeBase/src/FileTransferListModel.cpp b/AicsKnowledgeBase/src/FileTransferListModel.cpp index 67e9477..6cb46eb 100644 --- a/AicsKnowledgeBase/src/FileTransferListModel.cpp +++ b/AicsKnowledgeBase/src/FileTransferListModel.cpp @@ -3,20 +3,13 @@ FileTransferListModel::FileTransferListModel(QObject *parent) : QAbstractListModel(parent) { + m_roleName.insert(kDownloadRole, "download"); m_roleName.insert(kIdRole, "fileId"); m_roleName.insert(kNameRole, "name"); m_roleName.insert(kCompletedSizeRole, "completedSize"); m_roleName.insert(kTotalSizeRole, "totalSize"); m_roleName.insert(kSpeedRole, "speed"); m_roleName.insert(kPausedRole, "paused"); - - m_data = { - {"v", "vivo", 1200, 2000}, - {"m", "meizu", 1300, 1500}, - {"a", "apple", 1400, 1400}, - }; - - } @@ -36,6 +29,8 @@ QVariant FileTransferListModel::data(const QModelIndex &index, int role) const return QVariant(); switch (role) { + case kDownloadRole: + return m_data.value(index.row()).download; case kIdRole: return m_data.value(index.row()).id; case kNameRole: diff --git a/AicsKnowledgeBase/src/FileTransferListModel.h b/AicsKnowledgeBase/src/FileTransferListModel.h index 9fc0fdc..593ba04 100644 --- a/AicsKnowledgeBase/src/FileTransferListModel.h +++ b/AicsKnowledgeBase/src/FileTransferListModel.h @@ -7,6 +7,7 @@ struct FileItem { + bool download = true; QString id; QString name; int64_t completedSize; @@ -38,7 +39,8 @@ public: enum FileItemRole { - kIdRole = Qt::UserRole + 1, + kDownloadRole = Qt::UserRole + 1, + kIdRole, kNameRole, kCompletedSizeRole, kTotalSizeRole, diff --git a/AicsKnowledgeBase/src/FileTransferManager.cpp b/AicsKnowledgeBase/src/FileTransferManager.cpp index 55ef572..2f68638 100644 --- a/AicsKnowledgeBase/src/FileTransferManager.cpp +++ b/AicsKnowledgeBase/src/FileTransferManager.cpp @@ -13,6 +13,88 @@ static const std::string baseUrl = "https://api.hammer-hfut.tk:233/aics/file/"; //static const std::string baseUrl = "http://127.0.0.1:4523/m1/2914957-0-6e5f2db1/"; //static const std::string baseUrl = "https://quic.nginx.org/"; +struct FileDownloading : FileItem +{ + CURL *curlHandle = nullptr; + std::ofstream file; +}; +std::unordered_map downloadingMap; +std::mutex downloadingMapMutex; + +struct FileUploading : FileItem +{ + CURL *curlHandle = nullptr; + std::ifstream file; +}; + +std::unordered_map uploadingMap; +std::mutex uploadingMapMutex; + +size_t writeDataFunc(const void *ptr, size_t size, size_t nmemb, void *obj) +{ + auto buf = (FileDownloading *) obj; + auto writeSize = size * nmemb; + buf->file.write(reinterpret_cast(ptr), writeSize); + return writeSize; +} + +size_t readDataFunc(void *ptr, size_t size, size_t nmemb, void *obj) +{ + auto buf = (FileUploading *) obj; + auto readSize = size * nmemb; + buf->file.read(reinterpret_cast(ptr), readSize); + return readSize; +} + +static int xferinfo(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) +{ + auto fileTrans = (FileDownloading *) p; + curl_off_t speed = 0; + + curl_easy_getinfo(fileTrans->curlHandle, CURLINFO_SPEED_DOWNLOAD_T, &speed); + + fileTrans->completedSize += dlnow; + + fileTrans->speed = speed; + qDebug() << std::format("Downloading: {} / {}, Speed: {}", fileTrans->completedSize, fileTrans->totalSize, + speed).c_str(); + + auto item = static_cast(*fileTrans); + QTimer::singleShot(0, qApp, [item]() { + FileTransferListModel::instance().setItem(item); + }); + + if (fileTrans->paused) { + qDebug() << "Pause"; + return 1; + } + fileTrans->completedSize -= dlnow; + return 0; +} + +static int uploadInfo(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) +{ + auto fileTrans = (FileUploading *) p; + curl_off_t speed = 0; + curl_easy_getinfo(fileTrans->curlHandle, CURLINFO_SPEED_UPLOAD_T, &speed); + fileTrans->completedSize += ulnow; + fileTrans->speed = speed; + qDebug() << std::format("Uploading: {} / {}, Speed: {}", fileTrans->completedSize, fileTrans->totalSize, + speed).c_str(); + + auto item = static_cast(*fileTrans); + QTimer::singleShot(0, qApp, [item]() { + FileTransferListModel::instance().setItem(item); + }); + + if (fileTrans->paused) { + qDebug() << "Pause"; + return 1; + } + fileTrans->completedSize -= ulnow; + return 0; +} + static size_t writeDataCallback(void *contents, size_t size, size_t nmemb, void *userp) { try { @@ -60,74 +142,88 @@ FileTransferManager::FileTransferManager(QObject *parent) : QObject(parent) { std::string resData; - if (CURLE_OK == httpGet("file/hello", resData)) + if (CURLE_OK == httpGet("hello", resData)) qDebug() << "HTTP3 OK\n" << resData.c_str(); else qDebug() << "HTTP3 FAILED"; - } -int64_t completedSize = 0; - -size_t readDataFunc(void *ptr, size_t size, size_t nmemb, void *file) -{ - auto s = reinterpret_cast(file)->read(reinterpret_cast(ptr), - size * nmemb); - completedSize += s; - qDebug() << std::format("Uploading: {} / {}", completedSize, - reinterpret_cast(file)->size()).c_str(); - return s; -} - -void FileTransferManager::upload(const QUrl &fileUrl, const QString &fileId, const QString &ticket) +void +FileTransferManager::upload(const QUrl &fileUrl, const QString &fileId, const QString &ticket, const QString &fileName) { qDebug() << fileUrl.fileName(); - QFile file = fileUrl.toLocalFile(); - if (!file.open(QIODevice::ReadOnly)) - return; - auto fileSize = file.size(); + QtConcurrent::run([fileUrl, fileId, ticket, fileName] { - CURLcode res; - CURL *curl = curl_easy_init(); - if (!curl) - return; - curl_easy_setopt(curl, CURLOPT_URL, (baseUrl + "file/" + fileId.toStdString()).c_str()); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https"); - curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3ONLY); - curl_httppost *formpost = nullptr; - curl_httppost *lastptr = nullptr; - curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "ticket", - CURLFORM_COPYCONTENTS, ticket.toStdString().c_str(), - CURLFORM_END); + auto fileSize = QFileInfo(fileUrl.toLocalFile()).size(); - curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "rangeStart", - CURLFORM_COPYCONTENTS, std::to_string(0).c_str(), - CURLFORM_END); - curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "rangeEnd", - CURLFORM_COPYCONTENTS, std::to_string(fileSize).c_str(), - CURLFORM_END); + FileItem item{false, fileId, fileName, 0, fileSize}; + QTimer::singleShot(0, qApp, [item]() { + FileTransferListModel::instance().insertItem(item); + }); - curl_formadd(&formpost, &lastptr, - CURLFORM_COPYNAME, "data", - CURLFORM_STREAM, &file, - CURLFORM_CONTENTLEN, fileSize, - CURLFORM_END); - curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); - curl_easy_setopt(curl, CURLOPT_READFUNCTION, readDataFunc); - qDebug() << "Begin Upload"; - completedSize = 0; - res = curl_easy_perform(curl); - if (res == CURLE_OK) - qDebug() << "Upload Success"; - else - qDebug() << "Upload Failed"; - curl_easy_cleanup(curl); - curl_formfree(formpost); - file.close(); + uploadingMapMutex.lock(); + auto [iter, _] = uploadingMap.emplace(item.id, (FileUploading) item); + uploadingMapMutex.unlock(); + + FileUploading &fileUploading = iter->second; + fileUploading.file = std::ifstream(fileUrl.toLocalFile().toLocal8Bit().toStdString(), std::ios::binary); + + if (!fileUploading.file.is_open()) { + qDebug() << "Open File Failed"; + return false; + } + + CURLcode res; + fileUploading.curlHandle = curl_easy_init(); + if (!fileUploading.curlHandle) + return false; + + curl_httppost *formpost = nullptr; + curl_httppost *lastptr = nullptr; + curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "ticket", + CURLFORM_COPYCONTENTS, ticket.toStdString().c_str(), + CURLFORM_END); + + curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "rangeStart", + CURLFORM_COPYCONTENTS, std::to_string(0).c_str(), + CURLFORM_END); + curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "rangeEnd", + CURLFORM_COPYCONTENTS, std::to_string(fileSize).c_str(), + CURLFORM_END); + + curl_formadd(&formpost, &lastptr, + CURLFORM_COPYNAME, "data", + CURLFORM_STREAM, &fileUploading, + CURLFORM_CONTENTLEN, fileSize, + CURLFORM_END); + curl_easy_setopt(fileUploading.curlHandle, CURLOPT_URL, (baseUrl + fileId.toStdString()).c_str()); + curl_easy_setopt(fileUploading.curlHandle, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(fileUploading.curlHandle, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(fileUploading.curlHandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3ONLY); + curl_easy_setopt(fileUploading.curlHandle, CURLOPT_HTTPPOST, formpost); + curl_easy_setopt(fileUploading.curlHandle, CURLOPT_READFUNCTION, readDataFunc); + curl_easy_setopt(fileUploading.curlHandle, CURLOPT_NOPROGRESS, false); + curl_easy_setopt(fileUploading.curlHandle, CURLOPT_XFERINFOFUNCTION, uploadInfo); + curl_easy_setopt(fileUploading.curlHandle, CURLOPT_XFERINFODATA, &fileUploading); + + qDebug() << "Begin Upload"; + + res = curl_easy_perform(fileUploading.curlHandle); + if (res == CURLE_OK) + qDebug() << "Upload Success"; + else + qDebug() << "Upload Failed"; + curl_easy_cleanup(fileUploading.curlHandle); + curl_formfree(formpost); + fileUploading.file.close(); + + uploadingMapMutex.lock(); + uploadingMap.erase(fileId); + uploadingMapMutex.unlock(); + + }); } @@ -151,104 +247,53 @@ curl_off_t getHttpFileSize(const std::string &url) return fileLength; } -struct FileTransfering : FileItem -{ - CURL *curlHandle = nullptr; - std::ofstream file; -}; - -size_t receiveDataFunc(const void *ptr, size_t size, size_t nmemb, void *obj) -{ - auto buf = (FileTransfering *) obj; - auto writeSize = size * nmemb; - buf->file.write(reinterpret_cast(ptr), writeSize); - return writeSize; -} - -static int xferinfo(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) -{ - auto fileTrans = (FileTransfering *) p; - curl_off_t speed = 0; - - curl_easy_getinfo(fileTrans->curlHandle, CURLINFO_SPEED_DOWNLOAD_T, &speed); - - fileTrans->completedSize += dlnow; - - fileTrans->speed = speed; - qDebug() << std::format("Downloading: {} / {}, Speed: {}", fileTrans->completedSize, fileTrans->totalSize, - speed).c_str(); - - auto item = static_cast(*fileTrans); - QTimer::singleShot(0, qApp, [item]() { - FileTransferListModel::instance().setItem(item); - }); - - if (fileTrans->paused) { - qDebug() << "Pause"; - return 1; - } - - - fileTrans->completedSize -= dlnow; - - - return 0; -} - - -std::unordered_map transferMap; - -std::mutex transferMapMutex; - bool httpDownload(const std::string &url, const FileItem &item) { auto fileSize = getHttpFileSize(url);//获得文件大小。 - if (fileSize != -1) { - qDebug() << "FileSize: " << fileSize; - - transferMapMutex.lock(); - auto [iter, _] = transferMap.emplace(item.id, (FileTransfering) item); - transferMapMutex.unlock(); - - FileTransfering &obj = iter->second; - - obj.curlHandle = curl_easy_init(); - if (!obj.curlHandle) - return false; - obj.totalSize = fileSize;//原始文件大小 - obj.file = std::ofstream("D:\\Downloads\\" + obj.name.toStdString() + ".zip", std::ios::binary); - - if (!obj.file.is_open()) { - qDebug() << "Open File Failed"; - curl_easy_cleanup(obj.curlHandle); - return false; - } else { - - curl_easy_setopt(obj.curlHandle, CURLOPT_VERBOSE, 0L); - curl_easy_setopt(obj.curlHandle, CURLOPT_HEADER, 0); - curl_easy_setopt(obj.curlHandle, CURLOPT_NOPROGRESS, 1L); - curl_easy_setopt(obj.curlHandle, CURLOPT_NOSIGNAL, 1L); - curl_easy_setopt(obj.curlHandle, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(obj.curlHandle, CURLOPT_URL, url.c_str()); - curl_easy_setopt(obj.curlHandle, CURLOPT_WRITEDATA, (void *) &obj); - curl_easy_setopt(obj.curlHandle, CURLOPT_WRITEFUNCTION, receiveDataFunc); - curl_easy_setopt(obj.curlHandle, CURLOPT_NOPROGRESS, false);//设为false 下面才能设置进度响应函数 - curl_easy_setopt(obj.curlHandle, CURLOPT_XFERINFOFUNCTION, xferinfo); - curl_easy_setopt(obj.curlHandle, CURLOPT_XFERINFODATA, &obj); - - //下载 - auto res = curl_easy_perform(obj.curlHandle); - curl_easy_cleanup(obj.curlHandle); - obj.file.close(); - - transferMapMutex.lock(); - transferMap.erase(obj.id); - transferMapMutex.unlock(); - - return res == CURLE_OK; - } - } else + if (fileSize == -1) return false; + + qDebug() << "FileSize: " << fileSize; + + downloadingMapMutex.lock(); + auto [iter, _] = downloadingMap.emplace(item.id, (FileDownloading) item); + downloadingMapMutex.unlock(); + + FileDownloading &obj = iter->second; + + obj.curlHandle = curl_easy_init(); + if (!obj.curlHandle) + return false; + obj.totalSize = fileSize;//原始文件大小 + obj.file = std::ofstream("D:\\Downloads\\" + obj.name.toStdString() + ".zip", std::ios::binary); + + if (!obj.file.is_open()) { + qDebug() << "Open File Failed"; + curl_easy_cleanup(obj.curlHandle); + return false; + } + + curl_easy_setopt(obj.curlHandle, CURLOPT_VERBOSE, 0L); + curl_easy_setopt(obj.curlHandle, CURLOPT_HEADER, 0); + curl_easy_setopt(obj.curlHandle, CURLOPT_NOSIGNAL, 1L); + curl_easy_setopt(obj.curlHandle, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(obj.curlHandle, CURLOPT_URL, url.c_str()); + curl_easy_setopt(obj.curlHandle, CURLOPT_WRITEDATA, (void *) &obj); + curl_easy_setopt(obj.curlHandle, CURLOPT_WRITEFUNCTION, writeDataFunc); + curl_easy_setopt(obj.curlHandle, CURLOPT_NOPROGRESS, false);//设为false 下面才能设置进度响应函数 + curl_easy_setopt(obj.curlHandle, CURLOPT_XFERINFOFUNCTION, xferinfo); + curl_easy_setopt(obj.curlHandle, CURLOPT_XFERINFODATA, &obj); + + //下载 + auto res = curl_easy_perform(obj.curlHandle); + curl_easy_cleanup(obj.curlHandle); + obj.file.close(); + + downloadingMapMutex.lock(); + downloadingMap.erase(obj.id); + downloadingMapMutex.unlock(); + + return res == CURLE_OK; } void FileTransferManager::download(QString fileId) @@ -285,7 +330,7 @@ void FileTransferManager::download(QString fileId) int64_t size = getHttpFileSize(fileUrl); - FileItem item{fileId, fileId, 0, size}; + FileItem item{true, fileId, fileId, 0, size}; auto res = httpDownload(fileUrl, item); @@ -298,9 +343,9 @@ void FileTransferManager::download(QString fileId) void FileTransferManager::pause(const QString &fileId) { - transferMapMutex.lock(); - transferMap[fileId].paused = true; - transferMapMutex.unlock(); + downloadingMapMutex.lock(); + downloadingMap[fileId].paused = true; + downloadingMapMutex.unlock(); } QString FileTransferManager::getFileMd5(const QUrl &fileUrl) @@ -358,7 +403,7 @@ void FileTransferManager::resume(const QString &fileId) int64_t size = getHttpFileSize(fileUrl); - FileItem item{fileId, fileId, 0, size}; + FileItem item{true, fileId, fileId, 0, size}; QTimer::singleShot(0, qApp, [this, item]() { FileTransferListModel::instance().insertItem(item); }); @@ -371,3 +416,8 @@ void FileTransferManager::resume(const QString &fileId) } +QString FileTransferManager::getFileName(const QUrl &fileUrl) +{ + return fileUrl.fileName(); +} + diff --git a/AicsKnowledgeBase/src/FileTransferManager.h b/AicsKnowledgeBase/src/FileTransferManager.h index 2ef71d0..256aec0 100644 --- a/AicsKnowledgeBase/src/FileTransferManager.h +++ b/AicsKnowledgeBase/src/FileTransferManager.h @@ -19,9 +19,10 @@ Q_OBJECT public: explicit FileTransferManager(QObject *parent = nullptr); + Q_INVOKABLE QString getFileName(const QUrl& fileUrl); Q_INVOKABLE int64_t getFileSize(const QUrl& fileUrl); Q_INVOKABLE QString getFileMd5(const QUrl& fileUrl); - Q_INVOKABLE void upload(const QUrl& fileUrl, const QString& fileId, const QString& ticket); + Q_INVOKABLE void upload(const QUrl& fileUrl, const QString& fileId, const QString& ticket, const QString &fileName); Q_INVOKABLE void download(QString fileId); Q_INVOKABLE void pause(const QString& fileId);