diff --git a/AicsKnowledgeBase/qml/component/FileList.qml b/AicsKnowledgeBase/qml/component/FileList.qml index e992e3e..a5ae3b8 100644 --- a/AicsKnowledgeBase/qml/component/FileList.qml +++ b/AicsKnowledgeBase/qml/component/FileList.qml @@ -44,46 +44,10 @@ Item { "uuid": uuid }]) } - FluButton { - Layout.alignment: Qt.AlignRight - text: "上传" - onClicked: function () { - console.log("click") - fileDialog.open() - } - 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": name, - "brief": "brief", - "size": size, - "md5": md5, - "tags": [], - "parentId": header.items.length - !== 0 ? header.items[header.items.length - 1].uuid : null - } - console.log("begin") - console.log(JSON.stringify(body)) - Request.post("knowledge/file", JSON.stringify(body), - function (res, data) { - console.log(res) - console.log(data) - FileTransferManager.upload( - selectedFile, data.id, - data.ticket, name) - }, function (res, data) { - console.log(res) - }) - } - } + UploadButton { + header: header } + RowLayout { id: fileListItemHeaderItem width: ListView.view.width diff --git a/AicsKnowledgeBase/qml/component/FileListItem.qml b/AicsKnowledgeBase/qml/component/FileListItem.qml index 7516c72..2eaab06 100644 --- a/AicsKnowledgeBase/qml/component/FileListItem.qml +++ b/AicsKnowledgeBase/qml/component/FileListItem.qml @@ -39,13 +39,13 @@ FluArea { Image { id: icon source: type ? "qrc:/AicsKnowledgeBase/res/" + type + ".png" : "" - Layout.preferredHeight: 32 - Layout.preferredWidth: 32 + Layout.preferredHeight: 24 + Layout.preferredWidth: 24 } FluText { id: title font.bold: true - font.pointSize: 15 + font.pointSize: 12 text: fileItem.title } } diff --git a/AicsKnowledgeBase/qml/component/UploadButton.qml b/AicsKnowledgeBase/qml/component/UploadButton.qml new file mode 100644 index 0000000..200c825 --- /dev/null +++ b/AicsKnowledgeBase/qml/component/UploadButton.qml @@ -0,0 +1,146 @@ +import QtQuick 2.15 +import QtQuick.Layouts +import FluentUI +import QtQuick.Dialogs +import "qrc:///AicsKnowledgeBase/qml/global" +import SignalFileOperation 1.0 +import AicsKB.FileTransferManager + +FluButton { + property var header + property var currentSelectedFile: null + + Layout.alignment: Qt.AlignRight + text: "上传" + onClicked: function () { + console.log("click") + fileDialog.open() + } + FileDialog { + id: fileDialog + onAccepted: function () { + currentSelectedFile = selectedFile + popup.open() + } + onRejected: currentSelectedFile = null + } + + FluPopup { + id: popup + focus: true + Rectangle { + id: layout_content + anchors.fill: parent + implicitWidth: Window.window == null ? 400 : Math.min( + Window.window.width, 400) + + implicitHeight: text_title.height + text_message.height + layout_actions.height + color: 'transparent' + radius: 5 + FluText { + id: text_title + font: FluTextStyle.TitleLarge + text: "上传知识文件" + topPadding: 20 + leftPadding: 20 + rightPadding: 20 + wrapMode: Text.WrapAnywhere + anchors { + top: parent.top + left: parent.left + right: parent.right + } + } + FluText { + id: text_message + font: FluTextStyle.Body + wrapMode: Text.WrapAnywhere + text: "content" + topPadding: 14 + leftPadding: 20 + rightPadding: 20 + bottomPadding: 14 + anchors { + top: text_title.bottom + left: parent.left + right: parent.right + } + } + Rectangle { + id: layout_actions + height: 68 + radius: 5 + color: FluTheme.dark ? Qt.rgba( + 32 / 255, 32 / 255, 32 / 255, + blurBackground ? blurOpacity - 0.4 : 1) : Qt.rgba( + 243 / 255, 243 / 255, 243 / 255, + blurBackground ? blurOpacity - 0.4 : 1) + anchors { + top: text_message.bottom + left: parent.left + right: parent.right + } + RowLayout { + anchors { + centerIn: parent + margins: spacing + fill: parent + } + spacing: 15 + FluButton { + id: negative_btn + Layout.fillWidth: true + Layout.fillHeight: true + text: "取消" + onClicked: { + popup.close() + } + } + FluFilledButton { + id: positive_btn + Layout.fillWidth: true + Layout.fillHeight: true + text: "上传" + onClicked: { + popup.close() + let name = FileTransferManager.getFileName( + currentSelectedFile) + const size = FileTransferManager.getFileSize( + currentSelectedFile) + const md5 = FileTransferManager.getFileMd5( + currentSelectedFile) + if (size <= 0 || md5 === '') + return + var body = { + "name": name, + "brief": "brief", + "size": size, + "md5": md5, + "tags": [], + "parentId": currentParentId() + } + console.log("begin") + console.log(JSON.stringify(body)) + Request.post("knowledge/file", + JSON.stringify(body), + function (res, data) { + console.log(res) + console.log(data) + FileTransferManager.upload( + currentSelectedFile, + data.id, data.ticket, + name) + }, function (res, data) { + console.log(res) + }) + } + } + } + } + } + } + + function currentParentId() { + return header.items.length !== 0 ? header.items[header.items.length - 1].uuid : null + } +} diff --git a/AicsKnowledgeBase/src/FileTransferManager.cpp b/AicsKnowledgeBase/src/FileTransferManager.cpp index 2f68638..592df5c 100644 --- a/AicsKnowledgeBase/src/FileTransferManager.cpp +++ b/AicsKnowledgeBase/src/FileTransferManager.cpp @@ -30,6 +30,24 @@ struct FileUploading : FileItem std::unordered_map uploadingMap; std::mutex uploadingMapMutex; +CURL *initCurlWithCommonOptions() +{ + CURL *curl = curl_easy_init(); + if (!curl) { + qCritical() << "CURL INIT FAILED!"; + return nullptr; + } + curl_easy_setopt(curl, CURLOPT_VERBOSE, true); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3000); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true); + curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https"); + //curl_easy_setopt(easy_handle, CURLOPT_SSL_VERIFYPEER, 0L); + //curl_easy_setopt(easy_handle, CURLOPT_SSL_VERIFYHOST, 0L);//不验证证书和HOST + return curl; +} + + size_t writeDataFunc(const void *ptr, size_t size, size_t nmemb, void *obj) { auto buf = (FileDownloading *) obj; @@ -41,9 +59,8 @@ size_t writeDataFunc(const void *ptr, size_t size, size_t nmemb, void *obj) 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; + buf->file.read(reinterpret_cast(ptr), size * nmemb); + return buf->file.gcount(); } static int xferinfo(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) @@ -113,25 +130,18 @@ static size_t writeDataCallback(void *contents, size_t size, size_t nmemb, void } CURLcode -httpGet(const std::string &url, std::string &response, long httpVersion = CURL_HTTP_VERSION_3ONLY, int timeout = 3000) +httpGet(const std::string &url, std::string &response) { CURLcode res; // 获取easy handle - CURL *easy_handle = curl_easy_init(); + CURL *easy_handle = initCurlWithCommonOptions(); if (!easy_handle) return CURLE_FAILED_INIT; - // 设置easy handle属性 - curl_easy_setopt(easy_handle, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(easy_handle, CURLOPT_HTTP_VERSION, httpVersion); curl_easy_setopt(easy_handle, CURLOPT_URL, (baseUrl + url).c_str()); curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, writeDataCallback); - curl_easy_setopt(easy_handle, CURLOPT_TIMEOUT, timeout); curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, (void *) &response); - curl_easy_setopt(easy_handle, CURLOPT_FOLLOWLOCATION, 1L); - //curl_easy_setopt(easy_handle, CURLOPT_DEFAULT_PROTOCOL, "https"); - //curl_easy_setopt(easy_handle, CURLOPT_SSL_VERIFYPEER, 0L); - //curl_easy_setopt(easy_handle, CURLOPT_SSL_VERIFYHOST, 0L);//不验证证书和HOST + // 执行数据请求 res = curl_easy_perform(easy_handle); curl_easy_cleanup(easy_handle); @@ -148,12 +158,40 @@ FileTransferManager::FileTransferManager(QObject *parent) qDebug() << "HTTP3 FAILED"; } +bool uploadComplete(const QString &fileId, const QString &ticket) +{ + CURLcode res; + auto curlHandle = initCurlWithCommonOptions(); + if (!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_easy_setopt(curlHandle, CURLOPT_URL, (baseUrl + "File/" + fileId.toStdString() + "/complete").c_str()); + curl_easy_setopt(curlHandle, CURLOPT_HTTPPOST, formpost); + + res = curl_easy_perform(curlHandle); + curl_easy_cleanup(curlHandle); + curl_formfree(formpost); + return res == CURLE_OK; +} + void FileTransferManager::upload(const QUrl &fileUrl, const QString &fileId, const QString &ticket, const QString &fileName) { qDebug() << fileUrl.fileName(); QtConcurrent::run([fileUrl, fileId, ticket, fileName] { + std::string resData; + if (CURLE_OK != httpGet("File/" + fileId.toStdString() + "/status", resData)) + return false; + + qDebug() << resData.c_str(); + auto resJson = QJsonDocument::fromJson(resData.c_str()); + //if(!resJson["isCompleted"].toBool()) auto fileSize = QFileInfo(fileUrl.toLocalFile()).size(); @@ -176,7 +214,7 @@ FileTransferManager::upload(const QUrl &fileUrl, const QString &fileId, const QS } CURLcode res; - fileUploading.curlHandle = curl_easy_init(); + fileUploading.curlHandle = initCurlWithCommonOptions(); if (!fileUploading.curlHandle) return false; @@ -194,27 +232,33 @@ FileTransferManager::upload(const QUrl &fileUrl, const QString &fileId, const QS CURLFORM_END); curl_formadd(&formpost, &lastptr, - CURLFORM_COPYNAME, "data", + CURLFORM_COPYNAME, "file", + CURLFORM_FILENAME, fileName.toLocal8Bit().toStdString().c_str(), 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_URL, (baseUrl + "File/" + fileId.toStdString()).c_str()); 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); + curl_easy_setopt(fileUploading.curlHandle, CURLOPT_WRITEFUNCTION, writeDataCallback); + std::string response; + curl_easy_setopt(fileUploading.curlHandle, CURLOPT_WRITEDATA, (void *) &response); qDebug() << "Begin Upload"; + qDebug() << ticket.toStdString().c_str(); + qDebug() << std::to_string(fileSize).c_str(); + + res = curl_easy_perform(fileUploading.curlHandle); if (res == CURLE_OK) qDebug() << "Upload Success"; else qDebug() << "Upload Failed"; + qDebug() << response.c_str(); curl_easy_cleanup(fileUploading.curlHandle); curl_formfree(formpost); fileUploading.file.close(); @@ -223,6 +267,8 @@ FileTransferManager::upload(const QUrl &fileUrl, const QString &fileId, const QS uploadingMap.erase(fileId); uploadingMapMutex.unlock(); + if (res == CURLE_OK) + uploadComplete(fileId, ticket); }); } @@ -231,11 +277,10 @@ curl_off_t getHttpFileSize(const std::string &url) { curl_off_t fileLength = 0; - CURL *handle = curl_easy_init(); + CURL *handle = initCurlWithCommonOptions(); curl_easy_setopt(handle, CURLOPT_URL, url.c_str()); curl_easy_setopt(handle, CURLOPT_HEADER, 0); //只需要header头 curl_easy_setopt(handle, CURLOPT_NOBODY, 1); //不需要body - curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L); if (curl_easy_perform(handle) == CURLE_OK) curl_easy_getinfo(handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &fileLength); @@ -261,7 +306,7 @@ bool httpDownload(const std::string &url, const FileItem &item) FileDownloading &obj = iter->second; - obj.curlHandle = curl_easy_init(); + obj.curlHandle = initCurlWithCommonOptions(); if (!obj.curlHandle) return false; obj.totalSize = fileSize;//原始文件大小 @@ -273,10 +318,8 @@ bool httpDownload(const std::string &url, const FileItem &item) 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); @@ -377,7 +420,7 @@ void FileTransferManager::resume(const QString &fileId) qDebug() << "Start Get"; std::string resData; - if (CURLE_OK != httpGet("File/" + fileId.toStdString() + "/status", resData, CURL_HTTP_VERSION_1_1)) + if (CURLE_OK != httpGet("File/" + fileId.toStdString() + "/status", resData)) return; qDebug() << resData.c_str();