This commit is contained in:
zhuzichu 2023-07-26 17:48:35 +08:00
parent 502044ebd2
commit 42f058ca69
5 changed files with 277 additions and 180 deletions

View File

@ -7,152 +7,152 @@ import QtQuick.Dialogs
import FluentUI
import "qrc:///example/qml/component"
FluScrollablePage{
FluContentPage{
title:"Http"
FluHttp{
id:http_get
url:"https://api.github.com/search/repositories"
onStart: {
showLoading()
}
onFinish: {
hideLoading()
}
onError:
(status,errorString)=>{
console.debug(status+"->"+errorString)
showError(errorString)
}
onSuccess:
(result)=>{
window_result.result = result
window_result.show()
}
id:http
}
FluHttp{
id:http_post
url:"https://www.wanandroid.com/article/query/0/json"
onStart: {
showLoading()
}
onFinish: {
hideLoading()
}
onError:
(status,errorString)=>{
console.debug(status+"->"+errorString)
showError(errorString)
ListModel{
id:data_model
ListElement{
name:"Get请求"
onClickListener : function(){
var callable = {}
callable.onStart = function(){
showLoading()
}
callable.onFinish = function(){
hideLoading()
}
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
http.get("https://httpbingo.org/get",callable)
}
onSuccess:
(result)=>{
window_result.result = result
window_result.show()
}
ListElement{
name:"Post表单请求"
onClickListener : function(){
var callable = {}
callable.onStart = function(){
showLoading()
}
callable.onFinish = function(){
hideLoading()
}
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
var param = {}
param.custname = "朱子楚"
param.custtel = "1234567890"
param.custemail = "zhuzichu520@gmail.com"
http.post("https://httpbingo.org/post",callable,param)
}
}
ListElement{
name:"Post Json请求"
onClickListener : function(){
var callable = {}
callable.onStart = function(){
showLoading()
}
callable.onFinish = function(){
hideLoading()
}
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
var param = {}
param.custname = "朱子楚"
param.custtel = "1234567890"
param.custemail = "zhuzichu520@gmail.com"
http.postJson("https://httpbingo.org/post",callable,param)
}
}
ListElement{
name:"Post String请求"
onClickListener : function(){
var callable = {}
callable.onStart = function(){
showLoading()
}
callable.onFinish = function(){
hideLoading()
}
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
var param = "我命由我不由天"
http.postString("https://httpbingo.org/post",callable,param)
}
}
}
FluHttp{
id:http_download
url:"http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
// url:"https://www.w3school.com.cn/example/html5/mov_bbb.mp4"
onStart: {
btn_download.disabled = true
ListView{
id:list_view
width: 160
clip: true
anchors{
top: parent.top
topMargin: 20
bottom: parent.bottom
left: parent.left
}
onFinish: {
btn_download.disabled = false
btn_download.text = "下载文件"
text_file_size.text = ""
model:data_model
delegate: FluButton{
implicitWidth: ListView.view.width
implicitHeight: 30
text: model.name
onClicked: {
model.onClickListener()
}
}
onDownloadProgress:
(recv,total)=>{
var locale = Qt.locale()
var precent = (recv/total * 100).toFixed(0) + "%"
console.debug(precent)
btn_download.text = "下载中..."+precent
text_file_size.text = "%1/%2".arg(locale.formattedDataSize(recv)).arg(locale.formattedDataSize(total))
}
onError:
(status,errorString)=>{
showError(errorString)
}
onSuccess:
(result)=>{
showSuccess(result)
}
}
FluArea{
Layout.fillWidth: true
Layout.topMargin: 20
height: 160
paddings: 10
ColumnLayout{
spacing: 14
anchors.verticalCenter: parent.verticalCenter
FluButton{
text:"Get请求"
onClicked: {
http_get.get({q:"FluentUI"})
}
anchors{
top: list_view.top
bottom: list_view.bottom
left: list_view.right
right: parent.right
}
Flickable{
clip: true
id:scrollview
width: parent.width
height: parent.height
contentWidth: width
contentHeight: text_info.height
ScrollBar.vertical: FluScrollBar {}
FluText{
id:text_info
width: scrollview.width
wrapMode: Text.WrapAnywhere
padding: 14
}
FluButton{
text:"Post请求"
onClicked: {
http_post.post({k:"jitpack"})
}
}
RowLayout{
FluButton{
id:btn_download
text:disabled ? "下载中..." : "下载文件"
onClicked: {
file_dialog.open()
}
}
FluText{
id:text_file_size
Layout.alignment: Qt.AlignVCenter
}
}
}
}
FolderDialog {
id: file_dialog
currentFolder: StandardPaths.standardLocations(StandardPaths.DownloadLocation)[0]
onAccepted: {
var path = FluTools.toLocalPath(currentFolder)+ "/big_buck_bunny.mp4"
http_download.download(path)
}
}
Window{
property string result : ""
id:window_result
width: 600
height: 400
color: FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
Item{
anchors.fill: parent
Flickable{
id:scrollview
width: parent.width
height: parent.height
contentWidth: width
contentHeight: text_info.height
ScrollBar.vertical: FluScrollBar {}
FluText{
id:text_info
width: scrollview.width
wrapMode: Text.WrapAnywhere
text:window_result.result
padding: 14
}
}
}
}
}

View File

@ -6,6 +6,7 @@
#include <QUrlQuery>
#include <QHttpMultiPart>
#include <QJsonDocument>
#include "MainThread.h"
#include "FluApp.h"
FluHttp::FluHttp(QObject *parent)
@ -31,15 +32,15 @@ void FluHttp::handleReply(QNetworkReply* reply){
_cache.append(reply);
}
void FluHttp::post(QVariantMap params,QVariantMap headers){
void FluHttp::post(QString url,QJSValue callable,QVariantMap params,QVariantMap headers){
QVariantMap data = invokeIntercept(params,headers,"post").toMap();
QThreadPool::globalInstance()->start([=](){
Q_EMIT start();
onStart(callable);
for (int i = 0; i < retry(); ++i) {
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
QUrl url(_url);
QNetworkRequest request(url);
QUrl _url(url);
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QHttpMultiPart multiPart(QHttpMultiPart::FormDataType);
QString contentType = QString("multipart/form-data;boundary=%1").arg(multiPart.boundary());
@ -69,28 +70,30 @@ void FluHttp::post(QVariantMap params,QVariantMap headers){
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
Q_EMIT success(result);
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
Q_EMIT error(status,errorString);
onError(callable,status,errorString);
}
}
}
Q_EMIT finish();
onFinish(callable);
});
}
void FluHttp::postString(QString params,QVariantMap headers){
void FluHttp::postString(QString url,QJSValue callable,QString params,QVariantMap headers){
QVariantMap data = invokeIntercept(params,headers,"postString").toMap();
QThreadPool::globalInstance()->start([=](){
Q_EMIT start();
onStart(callable);
for (int i = 0; i < retry(); ++i) {
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
QUrl url(_url);
QNetworkRequest request(url);
QUrl _url(url);
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QString contentType = QString("text/plain;charset=utf-8");
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
QEventLoop loop;
QNetworkReply* reply = manager.post(request,params.toUtf8());
_cache.append(reply);
@ -106,27 +109,27 @@ void FluHttp::postString(QString params,QVariantMap headers){
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
Q_EMIT success(result);
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
Q_EMIT error(status,errorString);
onError(callable,status,errorString);
}
}
}
Q_EMIT finish();
onFinish(callable);
});
}
void FluHttp::postJson(QVariantMap params,QVariantMap headers){
void FluHttp::postJson(QString url,QJSValue callable,QVariantMap params,QVariantMap headers){
QVariantMap data = invokeIntercept(params,headers,"postJson").toMap();
QThreadPool::globalInstance()->start([=](){
Q_EMIT start();
onStart(callable);
for (int i = 0; i < retry(); ++i) {
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
QUrl url(_url);
QNetworkRequest request(url);
QUrl _url(url);
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QString contentType = QString("application/json;charset=utf-8");
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
@ -145,28 +148,28 @@ void FluHttp::postJson(QVariantMap params,QVariantMap headers){
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
Q_EMIT success(result);
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
Q_EMIT error(status,errorString);
onError(callable,status,errorString);
}
}
}
Q_EMIT finish();
onFinish(callable);
});
}
void FluHttp::get(QVariantMap params,QVariantMap headers){
void FluHttp::get(QString url,QJSValue callable,QVariantMap params,QVariantMap headers){
QVariantMap data = invokeIntercept(params,headers,"get").toMap();
QThreadPool::globalInstance()->start([=](){
Q_EMIT start();
for (int i = 0; i < retry(); ++i) {
onStart(callable);
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
QUrl url(_url);
addQueryParam(&url,data["params"].toMap());
QNetworkRequest request(url);
QUrl _url(url);
addQueryParam(&_url,data["params"].toMap());
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QEventLoop loop;
QNetworkReply* reply = manager.get(request);
@ -183,33 +186,33 @@ void FluHttp::get(QVariantMap params,QVariantMap headers){
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
Q_EMIT success(result);
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
Q_EMIT error(status,errorString);
onError(callable,status,errorString);
}
}
}
Q_EMIT finish();
onFinish(callable);
});
}
void FluHttp::download(QString path,QVariantMap params,QVariantMap headers){
void FluHttp::download(QString url,QJSValue callable,QString filePath,QVariantMap params,QVariantMap headers){
QVariantMap data = invokeIntercept(params,headers,"download").toMap();
QThreadPool::globalInstance()->start([=](){
Q_EMIT start();
onStart(callable);
QNetworkAccessManager manager;
QUrl url(_url);
addQueryParam(&url,data["params"].toMap());
QNetworkRequest request(url);
QUrl _url(url);
addQueryParam(&_url,data["params"].toMap());
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QSharedPointer<QFile> file(new QFile(path));
QSharedPointer<QFile> file(new QFile(filePath));
QIODevice::OpenMode mode = QIODevice::WriteOnly|QIODevice::Truncate;
if (!file->open(mode))
{
Q_EMIT error(-1,QString("Url: %1 %2 Non-Writable").arg(request.url().toString(),file->fileName()));
Q_EMIT finish();
onError(callable,-1,QString("Url: %1 %2 Non-Writable").arg(request.url().toString(),file->fileName()));
onFinish(callable);
return;
}
QEventLoop loop;
@ -219,22 +222,22 @@ void FluHttp::download(QString path,QVariantMap params,QVariantMap headers){
QPointer<QNetworkReply> reply = manager.get(request);
_cache.append(reply);
connect(reply,&QNetworkReply::downloadProgress,this,[=](qint64 bytesReceived, qint64 bytesTotal){
Q_EMIT downloadProgress(bytesReceived,bytesTotal);
onDownloadProgress(callable,bytesReceived,bytesTotal);
});
connect(reply,&QNetworkReply::readyRead,this,[=](){
file->write(reply->readAll());
});
loop.exec();
if (reply->error() == QNetworkReply::NoError) {
Q_EMIT success(path);
onSuccess(callable,filePath);
}else{
Q_EMIT error(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(),reply->errorString());
onError(callable,reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(),reply->errorString());
}
_cache.removeOne(reply);
file->close();
reply->deleteLater();
reply = nullptr;
Q_EMIT finish();
onFinish(callable);
});
}
@ -271,3 +274,35 @@ void FluHttp::addHeaders(QNetworkRequest* request,const QMap<QString, QVariant>&
request->setRawHeader(iter.key().toUtf8(), iter.value().toString().toUtf8());
}
}
void FluHttp::onStart(const QJSValue& callable){
QJSValue onStart = callable.property("onStart");
MainThread::post([=](){onStart.call();});
}
void FluHttp::onFinish(const QJSValue& callable){
QJSValue onFinish = callable.property("onFinish");
MainThread::post([=](){onFinish.call();});
}
void FluHttp::onError(const QJSValue& callable,int status,QString errorString){
QJSValue onError = callable.property("onError");
QJSValueList args;
args<<status<<errorString;
MainThread::post([=](){onError.call(args);});
}
void FluHttp::onSuccess(const QJSValue& callable,QString result){
QJSValueList args;
args<<result;
QJSValue onSuccess = callable.property("onSuccess");
MainThread::post([=](){onSuccess.call(args);});
}
void FluHttp::onDownloadProgress(const QJSValue& callable,qint64 recv, qint64 total){
QJSValueList args;
args<<static_cast<double>(recv);
args<<static_cast<double>(total);
QJSValue onDownloadProgress = callable.property("onDownloadProgress");
MainThread::post([=](){onDownloadProgress.call(args);});
}

View File

@ -10,7 +10,6 @@
class FluHttp : public QObject
{
Q_OBJECT
Q_PROPERTY_AUTO(QString,url);
Q_PROPERTY_AUTO(int,retry);
Q_PROPERTY_AUTO(int,timeout)
QML_NAMED_ELEMENT(FluHttp)
@ -19,19 +18,19 @@ private:
void handleReply(QNetworkReply* reply);
void addQueryParam(QUrl* url,const QMap<QString, QVariant>& params);
void addHeaders(QNetworkRequest* request,const QMap<QString, QVariant>& params);
void onStart(const QJSValue& callable);
void onFinish(const QJSValue& callable);
void onError(const QJSValue& callable,int status,QString errorString);
void onSuccess(const QJSValue& callable,QString result);
void onDownloadProgress(const QJSValue& callable,qint64 recv, qint64 total);
public:
explicit FluHttp(QObject *parent = nullptr);
~FluHttp();
Q_SIGNAL void start();
Q_SIGNAL void finish();
Q_SIGNAL void error(int status,QString errorString);
Q_SIGNAL void success(QString result);
Q_SIGNAL void downloadProgress(qint64 recv, qint64 total);
Q_INVOKABLE void get(QVariantMap params = {},QVariantMap headers = {});
Q_INVOKABLE void post(QVariantMap params = {},QVariantMap headers = {});
Q_INVOKABLE void postString(QString params = "",QVariantMap headers = {});
Q_INVOKABLE void postJson(QVariantMap params = {},QVariantMap headers = {});
Q_INVOKABLE void download(QString path,QVariantMap params = {},QVariantMap headers = {});
Q_INVOKABLE void get(QString url,QJSValue callable,QVariantMap params = {},QVariantMap headers = {});
Q_INVOKABLE void post(QString url,QJSValue callable,QVariantMap params = {},QVariantMap headers = {});
Q_INVOKABLE void postString(QString url,QJSValue callable,QString params = "",QVariantMap headers = {});
Q_INVOKABLE void postJson(QString url,QJSValue callable,QVariantMap params = {},QVariantMap headers = {});
Q_INVOKABLE void download(QString url,QJSValue callable,QString filePath,QVariantMap params = {},QVariantMap headers = {});
Q_INVOKABLE void cancel();
private:
QList<QPointer<QNetworkReply>> _cache;

39
src/MainThread.cpp Normal file
View File

@ -0,0 +1,39 @@
#include "MainThread.h"
#include <QGuiApplication>
#include <QMetaMethod>
std::shared_ptr<MainThread> MainThread::createShared(QObject* bindObject)
{
return std::shared_ptr<MainThread>(new MainThread(bindObject), [=](QObject* mainThread) {
mainThread->deleteLater();
});
}
MainThread::MainThread(QObject* bindObject)
: mBindObject(bindObject)
, mIgnoreNullObject(bindObject == nullptr)
{
qRegisterMetaType<std::function<void()>>("std::function<void()>");
auto mainUIThread = qApp->thread();
if (this->thread() != mainUIThread)
{
this->moveToThread(mainUIThread);
}
}
MainThread::~MainThread()
{
}
void MainThread::post(std::function<void()> func)
{
QMetaObject::invokeMethod(createShared().get(), "mainThreadSlot", Q_ARG(std::function<void()>, func));
}
void MainThread::mainThreadSlot(std::function<void()> func)
{
if ((mIgnoreNullObject || mBindObject) && func)
{
func();
}
}

24
src/MainThread.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef MAINTHREAD_H
#define MAINTHREAD_H
#include <QObject>
#include <QPointer>
#include <QDebug>
class MainThread : public QObject
{
Q_OBJECT
public:
static void post(std::function<void()> func);
~MainThread();
private:
static std::shared_ptr<MainThread> createShared(QObject* bindObject = nullptr);
private slots:
void mainThreadSlot(std::function<void()> func);
private:
MainThread(QObject* bindObject = nullptr);
QPointer<QObject> mBindObject;
bool mIgnoreNullObject{ false };
};
#endif // MAINTHREAD_H