diff --git a/example/qml-Qt6/page/T_Network.qml b/example/qml-Qt6/page/T_Network.qml index 6e4fedb3..92681849 100644 --- a/example/qml-Qt6/page/T_Network.qml +++ b/example/qml-Qt6/page/T_Network.qml @@ -2,8 +2,8 @@ import QtQuick import QtQuick.Layouts import QtQuick.Window import QtQuick.Controls -import QtQuick.Dialogs import FluentUI +import Qt.labs.platform import "qrc:///example/qml/component" FluContentPage{ @@ -348,6 +348,32 @@ FluContentPage{ file_dialog.open() } } + FluProgressButton{ + id:btn_download + implicitWidth: parent.width + implicitHeight: 36 + text: "Download File" + onClicked: { + folder_dialog.showDialog(function(path){ + FluNetwork.get("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4") + .toDownload(path) + .go(callable_download_file) + }) + } + } + FluProgressButton{ + id:btn_download_breakpoint + implicitWidth: parent.width + implicitHeight: 36 + text: "Breakpoint Download File" + onClicked: { + folder_dialog.showDialog(function(path){ + FluNetwork.get("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4") + .toDownload(path,true) + .go(callable_breakpoint_download_file) + }) + } + } } } @@ -375,6 +401,56 @@ FluContentPage{ } } + FluNetworkCallable{ + id:callable_download_file + onStart: { + btn_download.progress = 0 + btn_download.disabled = true + } + onFinish: { + btn_download.disabled = false + } + onError: + (status,errorString,result)=>{ + btn_download.progress = 0 + showError(errorString) + console.debug(status+";"+errorString+";"+result) + } + onSuccess: + (result)=>{ + showSuccess(result) + } + onDownloadProgress: + (recv,total)=>{ + btn_download.progress = recv/total + } + } + + FluNetworkCallable{ + id:callable_breakpoint_download_file + onStart: { + btn_download_breakpoint.progress = 0 + btn_download_breakpoint.disabled = true + } + onFinish: { + btn_download_breakpoint.disabled = false + } + onError: + (status,errorString,result)=>{ + btn_download_breakpoint.progress = 0 + showError(errorString) + console.debug(status+";"+errorString+";"+result) + } + onSuccess: + (result)=>{ + showSuccess(result) + } + onDownloadProgress: + (recv,total)=>{ + btn_download_breakpoint.progress = recv/total + } + } + FileDialog { id: file_dialog onAccepted: { @@ -386,6 +462,20 @@ FluContentPage{ } } + FileDialog { + property var onSelectListener + id: folder_dialog + currentFile: StandardPaths.standardLocations(StandardPaths.DownloadLocation)[0]+"/big_buck_bunny.mp4" + fileMode: FileDialog.SaveFile + onAccepted: { + folder_dialog.onSelectListener(FluTools.toLocalPath(folder_dialog.currentFile)) + } + function showDialog(listener){ + folder_dialog.onSelectListener = listener + folder_dialog.open() + } + } + FluArea{ anchors{ top: layout_flick.top diff --git a/example/qml/page/T_Network.qml b/example/qml/page/T_Network.qml index a2ad8934..409f114b 100644 --- a/example/qml/page/T_Network.qml +++ b/example/qml/page/T_Network.qml @@ -3,6 +3,7 @@ import QtQuick.Layouts 1.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15 import FluentUI 1.0 +import Qt.labs.platform 1.0 import "qrc:///example/qml/component" import "../component" @@ -339,6 +340,95 @@ FluContentPage{ .go(callable) } } + FluProgressButton{ + id:btn_upload + implicitWidth: parent.width + implicitHeight: 36 + text: "Upload File" + onClicked: { + file_dialog.open() + } + } + FluProgressButton{ + id:btn_download + implicitWidth: parent.width + implicitHeight: 36 + text: "Download File" + onClicked: { + folder_dialog.open() + } + } + } + } + + FluNetworkCallable{ + id:callable_upload_file + onStart: { + btn_upload.disabled = true + } + onFinish: { + btn_upload.disabled = false + } + onError: + (status,errorString,result)=>{ + btn_upload.progress = 0 + text_info.text = result + console.debug(result) + } + onSuccess: + (result)=>{ + text_info.text = result + } + onUploadProgress: + (sent,total)=>{ + btn_upload.progress = sent/total + } + } + + FluNetworkCallable{ + id:callable_download_file + onStart: { + btn_download.progress = 0 + btn_download.disabled = true + } + onFinish: { + btn_download.disabled = false + } + onError: + (status,errorString,result)=>{ + btn_download.progress = 0 + showError(errorString) + console.debug(status+";"+errorString+";"+result) + } + onSuccess: + (result)=>{ + showSuccess(result) + } + onDownloadProgress: + (recv,total)=>{ + btn_download.progress = recv/total + } + } + + FileDialog { + id: file_dialog + onAccepted: { + FluNetwork.postForm("https://httpbingo.org/post") + .setRetry(0)//请求失败后不重复请求 + .add("accessToken","12345678") + .addFile("file",FluTools.toLocalPath(file_dialog.selectedFile)) + .go(callable_upload_file) + } + } + + FileDialog { + id: folder_dialog + currentFile: StandardPaths.standardLocations(StandardPaths.DownloadLocation)[0]+"/big_buck_bunny.mp4" + fileMode: FileDialog.SaveFile + onAccepted: { + FluNetwork.get("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4") + .toDownload(FluTools.toLocalPath(folder_dialog.currentFile)) + .go(callable_download_file) } } diff --git a/src/FluNetwork.cpp b/src/FluNetwork.cpp index 97c92811..4b6bb344 100644 --- a/src/FluNetwork.cpp +++ b/src/FluNetwork.cpp @@ -130,6 +130,14 @@ QString NetworkParams::buildCacheKey(){ obj.insert("param",QString(QJsonDocument::fromVariant(_paramMap).toJson(QJsonDocument::Compact))); obj.insert("header",QString(QJsonDocument::fromVariant(_headerMap).toJson(QJsonDocument::Compact))); obj.insert("file",QString(QJsonDocument::fromVariant(_fileMap).toJson(QJsonDocument::Compact))); + if(_downloadParam){ + QJsonObject downObj; + QString _destPath; + bool _append; + downObj.insert("destPath",_downloadParam->_destPath); + downObj.insert("append",_downloadParam->_append); + obj.insert("download",downObj); + } QByteArray data = QJsonDocument(obj).toJson(QJsonDocument::Compact); return QCryptographicHash::hash(data, QCryptographicHash::Sha256).toHex(); } @@ -143,15 +151,14 @@ void NetworkParams::go(NetworkCallable* callable){ }else{ FluNetwork::getInstance()->handle(this,callable); } - } void FluNetwork::handle(NetworkParams* params,NetworkCallable* c){ - QString cacheKey = params->buildCacheKey(); QPointer callable(c); if(!callable.isNull()){ callable->start(); } + QString cacheKey = params->buildCacheKey(); if(params->_cacheMode == FluNetworkType::CacheMode::FirstCacheThenRequest && cacheExists(cacheKey)){ if(!callable.isNull()){ callable->cache(readCache(cacheKey)); @@ -209,8 +216,99 @@ void FluNetwork::handle(NetworkParams* params,NetworkCallable* c){ sendRequest(manager,request,params,reply,callable); } -void FluNetwork::handleDownload(NetworkParams* params,NetworkCallable* result){ +void FluNetwork::handleDownload(NetworkParams* params,NetworkCallable* c){ + QString cacheKey = params->buildCacheKey(); + QPointer callable(c); + if(!callable.isNull()){ + callable->start(); + } + QUrl url(params->_url); + QNetworkAccessManager* manager = new QNetworkAccessManager(); + QNetworkReply *reply = nullptr; + manager->setTransferTimeout(params->getTimeout()); + addQueryParam(&url,params->_queryMap); + QNetworkRequest request(url); + addHeaders(&request,params->_headerMap); + QString cachePath = getCacheFilePath(cacheKey); + QString destPath = params->_downloadParam->_destPath; + QFile* destFile = new QFile(destPath); + QFile* cacheFile = new QFile(cachePath); + bool isOpen = false; + qint64 seek = 0; + if(cacheFile->exists() && destFile->exists() && params->_downloadParam->_append){ + QJsonObject cacheInfo = QJsonDocument::fromJson(readCache(cacheKey).toUtf8()).object(); + qint64 fileSize = cacheInfo.value("fileSize").toDouble(); + qint64 contentLength = cacheInfo.value("contentLength").toDouble(); + if(fileSize == contentLength && destFile->size() == contentLength){ + if(!callable.isNull()){ + callable->downloadProgress(fileSize,contentLength); + callable->success(destPath); + callable->finish(); + } + return; + } + if(fileSize==destFile->size()){ + request.setRawHeader("Range", QString("bytes=%1-").arg(fileSize).toUtf8()); + seek = fileSize; + isOpen = destFile->open(QIODevice::WriteOnly|QIODevice::Append); + }else{ + isOpen = destFile->open(QIODevice::WriteOnly|QIODevice::Truncate); + } + }else{ + isOpen = destFile->open(QIODevice::WriteOnly|QIODevice::Truncate); + } + if(!isOpen){ + if(!callable.isNull()){ + callable->error(-1,"device not open",""); + callable->finish(); + } + return; + } + if(params->_downloadParam->_append){ + if (!cacheFile->open(QIODevice::WriteOnly|QIODevice::Truncate)) + { + if(!callable.isNull()){ + callable->error(-1,"cache file device not open",""); + callable->finish(); + } + return; + } + } + + reply = manager->get(request); + destFile->setParent(reply); + cacheFile->setParent(reply); + connect(reply,&QNetworkReply::readyRead,reply,[reply,seek,destFile,cacheFile,callable]{ + if (!reply || !destFile || reply->error() != QNetworkReply::NoError) + { + return; + } + QMap downInfo; + qint64 contentLength = reply->header(QNetworkRequest::ContentLengthHeader).toLongLong()+seek; + downInfo.insert("contentLength",contentLength); + QString eTag = reply->header(QNetworkRequest::ETagHeader).toString(); + downInfo.insert("eTag",eTag); + destFile->write(reply->readAll()); + destFile->flush(); + downInfo.insert("fileSize",destFile->size()); + if(cacheFile->isOpen()){ + cacheFile->resize(0); + cacheFile->write(QJsonDocument::fromVariant(QVariant(downInfo)).toJson().toBase64()); + cacheFile->flush(); + } + if(!callable.isNull()){ + callable->downloadProgress(destFile->size(),contentLength); + } + }); + connect(manager,&QNetworkAccessManager::finished,this,[params,manager,callable](QNetworkReply *reply){ + params->deleteLater(); + reply->deleteLater(); + manager->deleteLater(); + if(!callable.isNull()){ + callable->finish(); + } + }); } QString FluNetwork::readCache(const QString& key){ diff --git a/src/FluNetwork.h b/src/FluNetwork.h index 882f0f11..fe5d3a21 100644 --- a/src/FluNetwork.h +++ b/src/FluNetwork.h @@ -65,7 +65,7 @@ public: Q_INVOKABLE NetworkParams* setTimeout(int val); Q_INVOKABLE NetworkParams* setRetry(int val); Q_INVOKABLE NetworkParams* setCacheMode(int val); - Q_INVOKABLE NetworkParams* toDownload(QString destPath,bool append); + Q_INVOKABLE NetworkParams* toDownload(QString destPath,bool append = false); Q_INVOKABLE void go(NetworkCallable* result); QString buildCacheKey(); QString method2String();