yang.yongquan 2023-07-06 15:26:56 +08:00
commit f85d98082e
8 changed files with 415 additions and 74 deletions

View File

@ -2,6 +2,7 @@ import QtQuick 2.15
import QtQuick.Layouts
import FluentUI
import QtQuick.Dialogs
import QtQuick.Controls
import "qrc:///AicsKnowledgeBase/qml/global"
import SignalFileOperation 1.0
import AicsKB.FileTransferManager
@ -44,43 +45,76 @@ 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
RowLayout {
Layout.preferredWidth: parent.width
Item {
Layout.alignment: Qt.AlignRight
height: 28
width: 28
InputDialog {
id: dialog
title: "新建文件夹"
buttonFlags: FluContentDialog.PositiveButton
| FluContentDialog.NegativeButton
negativeText: "取消"
positiveText: "确定"
message: "请输入文件夹名称"
onPositiveClicked: text => {
console.log(text)
}
}
Image {
source: "qrc:/AicsKnowledgeBase/res/createFolder.png"
anchors.fill: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
dialog.open()
}
}
}
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)
})
}
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)
})
}
}
}
@ -151,6 +185,7 @@ Item {
=== 0 ? "null" : header.items[header.items.length - 1].uuid
Request.get("/knowledge/" + uuid, function (response) {
var data = JSON.parse(response)
console.log(response)
console.log(data.knowledgeFileAttribute)
fileListModel.clear()
var files = data.children
@ -186,7 +221,7 @@ Item {
}
fileListModel.append(modelItem)
}
console.log(fileListModel)
console.log(fileListModel.count)
listView.currentIndex = -1
})
}

View File

@ -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
}
}

View File

@ -0,0 +1,122 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Window
import FluentUI
FluPopup {
id: popup
property string title: "Title"
property string message: "Message"
property string neutralText: "Neutral"
property string negativeText: "Negative"
property string positiveText: "Positive"
signal neutralClicked
signal negativeClicked
signal positiveClicked(string text)
enum ButtonFlag {
NegativeButton = 1,
NeutralButton = 2,
PositiveButton = 4
}
property int buttonFlags: FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
property var minWidth: {
if (Window.window == null)
return 400
return Math.min(Window.window.width, 400)
}
focus: true
Rectangle {
id: layout_content
anchors.fill: parent
implicitWidth: minWidth
implicitHeight: text_title.height + text_message.height + layout_actions.height
color: 'transparent'
radius: 5
FluText {
id: text_title
font: FluTextStyle.TitleLarge
text: title
topPadding: 20
leftPadding: 20
rightPadding: 20
wrapMode: Text.WrapAnywhere
anchors {
top: parent.top
left: parent.left
right: parent.right
}
}
FluTextBox {
id: text_message
font: FluTextStyle.Body
wrapMode: Text.WrapAnywhere
placeholderText: message
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: neutral_btn
Layout.fillWidth: true
Layout.fillHeight: true
visible: popup.buttonFlags & FluContentDialog.NeutralButton
text: neutralText
onClicked: {
popup.close()
neutralClicked()
}
}
FluButton {
id: negative_btn
Layout.fillWidth: true
Layout.fillHeight: true
visible: popup.buttonFlags & FluContentDialog.NegativeButton
text: negativeText
onClicked: {
popup.close()
negativeClicked()
}
}
FluFilledButton {
id: positive_btn
Layout.fillWidth: true
Layout.fillHeight: true
visible: popup.buttonFlags & FluContentDialog.PositiveButton
text: positiveText
onClicked: {
popup.close()
positiveClicked(text_message.text)
}
}
}
}
}
}

View File

@ -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
}
}

View File

@ -208,9 +208,7 @@ FluArea {
}
onClicked: {
emit: content_area.download(content_area.knowledgeFileId)
// FileTransferManager.download(content_area.knowledgeFileId)
FileTransferManager.download(
"4973c59e-3ba1-41f2-a57e-3b53c2b5e2a4")
FileTransferManager.download(content_area.knowledgeFileId)
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -30,6 +30,24 @@ struct FileUploading : FileItem
std::unordered_map<QString, FileUploading> 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<char *>(ptr), readSize);
return readSize;
buf->file.read(reinterpret_cast<char *>(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);
@ -296,11 +339,8 @@ bool httpDownload(const std::string &url, const FileItem &item)
return res == CURLE_OK;
}
void FileTransferManager::download(QString fileId)
void FileTransferManager::download(const QString& fileId)
{
static int _fileId = 0;
//fileId = QString::number(_fileId++);
QtConcurrent::run([fileId, this] {
qDebug() << "Start Get";
@ -321,7 +361,7 @@ void FileTransferManager::download(QString fileId)
FileTransferListModel::instance().insertItem(item);
});*/
auto fileUrl = "https://curl.se/download/curl-8.1.2.zip";
auto fileUrl = "File/" + fileId.toStdString();
/* curl_slist *headers = nullptr;
@ -377,7 +417,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();

View File

@ -24,7 +24,7 @@ public:
Q_INVOKABLE QString getFileMd5(const QUrl& fileUrl);
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 download(const QString& fileId);
Q_INVOKABLE void pause(const QString& fileId);
Q_INVOKABLE void resume(const QString& fileId);