目录
7、qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization faile
在云开发中主要的http api有云函数、数据库、存储,所以就以此各取典型试验了一下
详情请见:微信官方文档-HTTP API文档
非常感谢一位博主的文章,当时这个涉及到了知识盲区QHttpMultiPart,还好看到了他的博客才知道:Qt之QHttpMultiPart(上传文件到微信小程序云服务器)
本文主要就是抄袭上面两位
使用的net_p是自己封装的类,内容在末尾
1、接口调用凭证
详情请见:微信官方文档-auth.getAccessToken
获取小程序全局唯一后台接口调用凭据(access_token
)。调用绝大多数后台接口时都需使用 access_token,开发者需要进行妥善保存。通过开发着的appid和secret获取
1.1、请求地址
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
1.2、请求参数
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
grant_type | string | 是 | 填写 client_credential | |
appid | string | 是 | 小程序唯一凭证,即 AppID,可在「微信公众平台 - 设置 - 开发设置」页中获得。(需要已经成为开发者,且帐号没有异常状态) | |
secret | string | 是 | 小程序唯一凭证密钥,即 AppSecret,获取方式同 appid |
1.3、 返回值
属性 | 类型 | 说明 |
---|---|---|
access_token | string | 获取到的凭证 |
expires_in | number | 凭证有效时间,单位:秒。目前是7200秒之内的值。 |
errcode | number | 错误码 |
errmsg | string | 错误信息 |
1.4、access_token 的存储与更新
access_token
的存储至少要保留 512 个字符空间;access_token
的有效期目前为 2 个小时,需定时刷新,重复获取将导致上次获取的access_token
失效;
1.5、代码示例
1.5.1、申请定时器
因为你要两个小时刷新一次
Token_timer = new QTimer(this);
connect(Token_timer, &QTimer::timeout, this, &MainWindow::GetAccessToken);
Token_timer->stop();
1.5.2、GetAccessToken定义
/* 开发者小程序后台的appid */
QString appid = "xxxxxxxx";
/* 开发者小程序后台的secret */
QString secret = "xxxxxxxx";
QString url = (tr("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%1&secret=%2").arg(appid,secret));
ui->textBrowser->append("获取微信后台接口调用凭据ACCESS_TOKEN");
if (net_p->PostHttp(url,"", 1)) {
QJsonDocument jd = net_p->getData();
Access_Token = jd["access_token"].toString();
int Access_Token_expries = jd["expires_in"].toInt()-200;
ui->textBrowser->append("获取微信后台接口调用凭据ACCESS_TOKEN成功");
ui->textBrowser->append("Access_Token: "+ Access_Token);
Token_timer->setInterval(Access_Token_expries*1000);
if(!Token_timer->isActive()){
Token_timer->start();
}
}else{
ui->textBrowser->append("获取微信后台接口调用凭据ACCESS_TOKEN失败");
}
2、触发云函数
2.1、请求地址
POST https://api.weixin.qq.com/tcb/invokecloudfunction?access_token=ACCESS_TOKEN&env=ENV&name=FUNCTION_NAME
2.2、请求参数
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
access_token / cloudbase_access_token | string | 是 | 接口调用凭证 | |
env | string | 是 | 云开发环境ID | |
name | string | 是 | 云函数名称 | |
POSTBODY | string | 是 | 云函数的传入参数,具体结构由开发者定义。 |
2.3、返回值
返回的 JSON 数据包
属性 | 类型 | 说明 |
---|---|---|
errcode | number | 错误码 |
errmsg | string | 错误信息 |
resp_data | string | 云函数返回的buffer |
2.4、Tips
- 使用本 API 触发云函数,在云函数中无法获取 OpenID 等用户相关信息,无法使用涉及用户登录态的其他API。
- 注意 POST BODY 部分会传递给云函数作为输入参数。
- 由 HTTP API 触发的云函数可以使用云调用。
- 由 HTTP API 触发云函数的超时时间为5s,请注意云函数的执行时间不能过长。
2.5、代码示例
/* 函数功能:获取触发云函数 */
#define POSTBODY_NULL 0
void MainWindow::on_BtnTriggerCloud_clicked()
{
QString url = "https://api.weixin.qq.com/tcb/invokecloudfunction?";
/* 1、一切前提:access_token已获取且有效 */
url.append("access_token=").append(Access_Token).append("&");
/* 2、需要触发的云函数的环境变量 */
url.append("env=").append("xxxxxxxx").append("&");
/* 3、需要触发的云函数的名称 */
url.append("name=").append("GetOrderData");
QByteArray oby("");
#if POSTBODY_NULL
/* 4.1、POSTBODY为空 */
#else
/* 4.2、POSTBODY不为空,自定义obj内容 */
QJsonObject obj{
{"Type","Get"},
{"userOpenid","12345678"}
};
oby = QJsonDocument(obj).toJson();
#endif
if (net_p->PostHttp(url,oby,5)) {
QJsonDocument jd = net_p->getData();
ui->textBrowser->append(jd.toJson());
}else{
ui->textBrowser->append("触发微信后台云函数失败");
}
}
此处的env和name必须在url里
3、数据库内容导出
3.1、请求地址
POST https://api.weixin.qq.com/tcb/databasemigrateexport?access_token=ACCESS_TOKEN
3.2、请求参数
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
access_token / cloudbase_access_token | string | 是 | 接口调用凭证 | |
env | string | 是 | 云环境ID | |
file_path | string | 是 | 导出文件路径(文件会导出到公共的云存储中,可使用获取下载链接 API 获取下载链接) | |
file_type | number | 是 | 导出文件类型,文件格式参考数据库导入指引中的文件格式部分: 1:JSON 2:CSV | |
query | string | 是 | 导出条件 |
3.3、返回值
返回的 JSON 数据包
属性 | 类型 | 说明 |
---|---|---|
errcode | number | 错误码 |
errmsg | string | 错误信息 |
job_id | number | 导出任务ID,使用数据库迁移进度查询 API 查询导出结果,获取文件下载链接。 |
3.4、导出流程
- 申请导出,返回job_id
- 使用job_id查询迁移是否完成
- 等待迁移完成,时间较长,数据很少也需要几秒钟,所以需要定时器
- 迁移完成,返回临时文件的url,允许下载
3.5、代码示例
3.5.1、申请定时器
file_timer = new QTimer(this);
connect(file_timer, &QTimer::timeout, this, &MainWindow::GetFileDownloadUrl);
file_timer->setInterval(1000);
file_timer->stop();
3.5.2、申请导出,获取job_id
QString url = "https://api.weixin.qq.com/tcb/databasemigrateexport?";
/* 1、一切前提:access_token已获取且有效 */
url.append("access_token=").append(Access_Token);
QString query = tr("db.collection('Orders').get()");
QJsonObject obj{
{"env","xxxxxxxx"},//云开发的环境变量
{"file_path","temp.json"},
{"file_type",1},//1:json格式;2:csv格式
{"query",query}
};
QByteArray oby = QJsonDocument(obj).toJson();
if (net_p->PostHttp(url,oby,3)) {
QJsonDocument jd = net_p->getData();
job_id= jd["job_id"].toInt();
ui->textBrowser->append(jd.toJson());
ui->textBrowser->append("job_id: "+ QString::number(job_id));
file_timer->start();
}else{
ui->textBrowser->append("导出中获取job_id异常");
}
3.5.3、查询是否迁移完成,完成就下载
void MainWindow::GetFileDownloadUrl()
{
QString url = "https://api.weixin.qq.com/tcb/databasemigratequeryinfo?";
url.append("access_token=").append(Access_Token);
QJsonObject obj{
{"env","xxxxxxx"},//云开发的环境变量
{"job_id",job_id},
};
QByteArray oby = QJsonDocument(obj).toJson();
if (net_p->PostHttp(url,oby,3)) {
QJsonDocument jd = net_p->getData();
file_url = jd["file_url"].toString();
ui->textBrowser->append(jd.toJson());
ui->textBrowser->append(file_url);
if(!file_url.isEmpty()){
file_timer->stop();
ui->textBrowser->append("导出中获取文件下载进行状态:"+jd["status"].toString());
ui->textBrowser->append("导出中获取文件下载失败个数:"+QString::number(jd["record_fail"].toInt()));
ui->textBrowser->append("导出中获取文件下载成功个数:"+QString::number(jd["record_success"].toInt()));
/* 注意:此处开始变成get而不是post */
if (net_p->GetHttp(file_url,10)) {
QString path = QDir::currentPath()+"/temp.json";
QFile file(path);
file.open(QIODevice::WriteOnly);
file.write(net_p->getArray());
file.close();
ui->textBrowser->append("导出中下载文件完成");
}else{
ui->textBrowser->append("导出中下载文件异常");
}
}else{
ui->textBrowser->append("导出中获取文件下载进行状态:"+jd["status"].toString());
}
}else{
ui->textBrowser->append("导出中获取文件下载地址异常");
}
}
4、插入内容到数据库
4.1、请求地址
POST https://api.weixin.qq.com/tcb/databaseadd?access_token=ACCESS_TOKEN
4.2、请求参数
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
access_token / cloudbase_access_token | string | 是 | 接口调用凭证 | |
env | string | 是 | 云环境ID | |
query | string | 是 | 数据库操作语句 |
4.3、返回值
属性 | 类型 | 说明 |
---|---|---|
errcode | number | 错误码 |
errmsg | string | 错误信息 |
id_list | Array.<string> | 插入成功的数据集合主键_id |
4.4、 代码示例
QString url = "https://api.weixin.qq.com/tcb/databaseadd?";
/* 1、一切前提:access_token已获取且有效 */
url.append("access_token=").append(Access_Token);
QString query = tr("db.collection('Orders').add({\
data: {\
Address: \"杭州市富阳区金桥北路1号东方茂F1\",\
Name: \"肯德基(东方茂餐厅)\",\
Phone: \"0571-63371023\",\
PerCapita: \"31.0\",\
Comment: \"12\"}\
})");
QJsonObject obj{
{"env","xxxxxxxxx"},
{"query",query}
};
QByteArray oby = QJsonDocument(obj).toJson();
if (net_p->PostHttp(url,oby,3)) {
QJsonDocument jd = net_p->getData();
ui->textBrowser->append(jd.toJson());
int errcode = jd["errcode"].toInt();
if(errcode == 0){
ui->textBrowser->append("数据库插入成功");
}else{
ui->textBrowser->append("数据库插入异常");
}
}else{
ui->textBrowser->append("数据库插入发送异常");
}
5、获取文件上传链接
5.1、请求地址
POST https://api.weixin.qq.com/tcb/uploadfile?access_token=ACCESS_TOKEN
5.2、请求参数
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
access_token / cloudbase_access_token | string | 是 | 接口调用凭证 | |
env | string | 是 | 云环境ID | |
path | string | 是 | 上传路径 |
5.3、返回值
属性 | 类型 | 说明 |
---|---|---|
errcode | number | 错误码 |
errmsg | string | 错误信息 |
url | string | 上传url |
token | string | token |
authorization | string | authorization |
file_id | string | 文件ID |
cos_file_id | string | cos文件ID |
5.4、上传链接使用说明
用户获取到返回数据后,需拼装一个 HTTP POST 请求,其中 url 为返回包的 url 字段,Body 部分格式为 multipart/form-data,具体内容如下:
key | value | 说明 |
---|---|---|
key | this/is/a/example/file.path | 请求包中的 path 字段 |
Signature | q-sign-algorithm=sha1&q-ak=AKID9... | 返回数据的 authorization 字段 |
x-cos-security-token | Cukha70zkXIBqkh1Oh... | 返回数据的 token 字段 |
x-cos-meta-fileid | HDze32/qZENCwWi5... | 返回数据的 cos_file_id 字段 |
file | 文件内容 | 文件的二进制内容 |
这里真的太重要了,这个如果对于小程序来说的访问其实很简单,但是在Qt上需要使用 QHttpMultiPart
QString url = "https://api.weixin.qq.com/tcb/uploadfile?";
/* 1、一切前提:access_token已获取且有效 */
url.append("access_token=").append(Access_Token);
QString path = "temp.json";//云存储的文件路径
QJsonObject obj{
{"env","xxxxxx"},
{"path",path}/* 自定义文件路径 */
};
QByteArray oby = QJsonDocument(obj).toJson();
if (net_p->PostHttp(url,oby,3)) {
QJsonDocument jd = net_p->getData();
int errcode = jd["errcode"].toInt();
ui->textBrowser->append(jd.toJson());
if(errcode == 0){
ui->textBrowser->append("文件上传链接返回成功");
QFile f("D:/temp.json");//本地需要上传的文件路径
f.open(QIODevice::ReadOnly);
QByteArray ba = f.readAll();
f.close();
/* 没有返回值,也不知道为什么,文档没讲的我也不会啊 */
net_p->PostHttpMultiPart(path,ba,jd);
}else{
ui->textBrowser->append("文件上传链接返回异常");
}
}else{
ui->textBrowser->append("文件上传链接发送异常");
}
6、全部代码
6.1、mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QTimer>
#include <QMainWindow>
#include <QNetworkRequest>
#include <QNetworkAccessManager>
class QNetworkReply;
class QNetworkAccessManager;
class NetCommunication;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void GetAccessToken();
void GetFileDownloadUrl();
void on_BtnAccessToken_clicked();
void on_BtnTriggerCloud_clicked();
void on_BtnExport_clicked();
void on_BtnUpload_clicked();
void on_BtnInsert_clicked();
private:
Ui::MainWindow *ui;
NetCommunication *net_p;
QTimer *Token_timer;
QTimer *file_timer;
int Access_Token_expries;
QString Access_Token;
QString file_url;
int job_id;
};
#endif // MAINWINDOW_H
6.2、mainwindow.c
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtNetwork>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include "netcommunication.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
net_p = new NetCommunication(this);
Token_timer = new QTimer(this);
connect(Token_timer, &QTimer::timeout, this, &MainWindow::GetAccessToken);
Token_timer->stop();
file_timer = new QTimer(this);
connect(file_timer, &QTimer::timeout, this, &MainWindow::GetFileDownloadUrl);
file_timer->setInterval(1000);
file_timer->stop();
Access_Token = "";
job_id = 0;
}
MainWindow::~MainWindow()
{
delete ui;
delete net_p;
}
/* access_token 的存储与更新
* access_token 的存储至少要保留 512 个字符空间;
* access_token 的有效期目前为 2 个小时,需定时刷新,重复获取将导致上次获取的 access_token 失效;
* 建议开发者使用中控服务器统一获取和刷新 access_token,其他业务逻辑服务器所使用的 access_token 均来自于该中控服务器,不应该各自去刷新,否则容易造成冲突,导致 access_token 覆盖而影响业务;
* access_token 的有效期通过返回的 expires_in 来传达,目前是7200秒之内的值,中控服务器需要根据这个有效时间提前去刷新。在刷新过程中,中控服务器可对外继续输出的老 access_token,此时公众平台后台会保证在5分钟内,新老 access_token 都可用,这保证了第三方业务的平滑过渡;
* access_token 的有效时间可能会在未来有调整,所以中控服务器不仅需要内部定时主动刷新,还需要提供被动刷新 access_token 的接口,这样便于业务服务器在 API 调用获知 access_token 已超时的情况下,可以触发 access_token 的刷新流程。
*/
void MainWindow::GetAccessToken(){
/* 开发者小程序后台的appid */
QString appid = "xxxxxxxx";
/* 开发者小程序后台的secret */
QString secret = "xxxxxxxx";
QString url = (tr("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%1&secret=%2").arg(appid,secret));
ui->textBrowser->append("获取微信后台接口调用凭据ACCESS_TOKEN");
if (net_p->PostHttp(url,"", 1)) {
QJsonDocument jd = net_p->getData();
Access_Token = jd["access_token"].toString();
int Access_Token_expries = jd["expires_in"].toInt()-200;
ui->textBrowser->append("获取微信后台接口调用凭据ACCESS_TOKEN成功");
ui->textBrowser->append("Access_Token: "+ Access_Token);
ui->textBrowser->append("Access_Token_expries: "+ QString::number(Access_Token_expries));
ui->textBrowser->append(jd.toJson());
Token_timer->setInterval(Access_Token_expries*1000);
if(!Token_timer->isActive()){
Token_timer->start();
}
}else{
ui->textBrowser->append("获取微信后台接口调用凭据ACCESS_TOKEN失败");
}
}
/* 函数功能:获取微信后台接口 */
void MainWindow::on_BtnAccessToken_clicked()
{
GetAccessToken();
}
/* 函数功能:获取触发云函数 */
#define POSTBODY_NULL 0
void MainWindow::on_BtnTriggerCloud_clicked()
{
QString url = "https://api.weixin.qq.com/tcb/invokecloudfunction?";
/* 1、一切前提:access_token已获取且有效 */
url.append("access_token=").append(Access_Token).append("&");
/* 2、需要触发的云函数的环境变量 */
url.append("env=").append("xxxxxxxx").append("&");
/* 3、需要触发的云函数的名称 */
url.append("name=").append("GetOrderData");
QByteArray oby("");
#if POSTBODY_NULL
/* 4.1、POSTBODY为空 */
#else
/* 4.2、POSTBODY不为空,自定义obj内容 */
QJsonObject obj{
{"Type","Get"},
{"userOpenid","12345678"}
};
qDebug() << "obj : " << obj;
qDebug() << "oby : " << oby;
oby = QJsonDocument(obj).toJson();
#endif
if (net_p->PostHttp(url,oby,3)) {
QJsonDocument jd = net_p->getData();
ui->textBrowser->append(jd.toJson());
}else{
ui->textBrowser->append("触发微信后台云函数失败");
}
}
void MainWindow::GetFileDownloadUrl()
{
QString url = "https://api.weixin.qq.com/tcb/databasemigratequeryinfo?";
url.append("access_token=").append(Access_Token);
QJsonObject obj{
{"env","xxxxxxxx"},
{"job_id",job_id},
};
QByteArray oby = QJsonDocument(obj).toJson();
if (net_p->PostHttp(url,oby,3)) {
QJsonDocument jd = net_p->getData();
file_url = jd["file_url"].toString();
ui->textBrowser->append(jd.toJson());
ui->textBrowser->append(file_url);
if(!file_url.isEmpty()){
file_timer->stop();
ui->textBrowser->append("导出中获取文件下载进行状态:"+jd["status"].toString());
ui->textBrowser->append("导出中获取文件下载失败个数:"+QString::number(jd["record_fail"].toInt()));
ui->textBrowser->append("导出中获取文件下载成功个数:"+QString::number(jd["record_success"].toInt()));
/* 注意:此处开始变成get而不是post */
if (net_p->GetHttp(file_url,10)) {
QString path = QDir::currentPath()+"/temp.json";
QFile file(path);
file.open(QIODevice::WriteOnly);
file.write(net_p->getArray());
file.close();
ui->textBrowser->append("导出中下载文件完成");
}else{
ui->textBrowser->append("导出中下载文件异常");
}
}else{
ui->textBrowser->append("导出中获取文件下载进行状态:"+jd["status"].toString());
}
}else{
ui->textBrowser->append("导出中获取文件下载地址异常");
}
}
void MainWindow::on_BtnExport_clicked()
{
QString url = "https://api.weixin.qq.com/tcb/databasemigrateexport?";
/* 1、一切前提:access_token已获取且有效 */
url.append("access_token=").append(Access_Token);
QString query = tr("db.collection('Orders').get()");
QJsonObject obj{
{"env","xxxxxxxx"},
{"file_path","temp.json"},
{"file_type",1},//1:json格式;2:csv格式
{"query",query}
};
QByteArray oby = QJsonDocument(obj).toJson();
if (net_p->PostHttp(url,oby,3)) {
QJsonDocument jd = net_p->getData();
job_id= jd["job_id"].toInt();
ui->textBrowser->append(jd.toJson());
ui->textBrowser->append("job_id: "+ QString::number(job_id));
file_timer->start();
}else{
ui->textBrowser->append("导出中获取job_id异常");
}
}
void MainWindow::on_BtnUpload_clicked()
{
QString url = "https://api.weixin.qq.com/tcb/uploadfile?";
/* 1、一切前提:access_token已获取且有效 */
url.append("access_token=").append(Access_Token);
QString path = "temp.json";
QJsonObject obj{
{"env","xxxxxxxx"},
{"path",path}/* 自定义文件路径 */
};
QByteArray oby = QJsonDocument(obj).toJson();
if (net_p->PostHttp(url,oby,3)) {
QJsonDocument jd = net_p->getData();
int errcode = jd["errcode"].toInt();
ui->textBrowser->append(jd.toJson());
if(errcode == 0){
ui->textBrowser->append("文件上传链接返回成功");
QFile f("D:/temp.json");
f.open(QIODevice::ReadOnly);
QByteArray ba = f.readAll();
f.close();
/* 没有返回值,也不知道为什么,文档没讲的我也不会啊 */
net_p->PostHttpMultiPart(path,ba,jd);
}else{
ui->textBrowser->append("文件上传链接返回异常");
}
}else{
ui->textBrowser->append("文件上传链接发送异常");
}
}
/*
* Address: 杭州市富阳区金桥北路1号东方茂F1
Name: 肯德基(东方茂餐厅)
Phone: 0571-63371023
PerCapita: 31.0
Comment: 12
*/
void MainWindow::on_BtnInsert_clicked()
{
QString url = "https://api.weixin.qq.com/tcb/databaseadd?";
/* 1、一切前提:access_token已获取且有效 */
url.append("access_token=").append(Access_Token);
QString query = tr("db.collection('Orders').add({\
data: {\
Address: \"杭州市富阳区金桥北路1号东方茂F1\",\
Name: \"肯德基(东方茂餐厅)\",\
Phone: \"0571-63371023\",\
PerCapita: \"31.0\",\
Comment: \"12\"}\
})");
QJsonObject obj{
{"env","xxxxxxxx"},
{"query",query}
};
QByteArray oby = QJsonDocument(obj).toJson();
if (net_p->PostHttp(url,oby,3)) {
QJsonDocument jd = net_p->getData();
ui->textBrowser->append(jd.toJson());
int errcode = jd["errcode"].toInt();
if(errcode == 0){
ui->textBrowser->append("数据库插入成功");
}else{
ui->textBrowser->append("数据库插入异常");
}
}else{
ui->textBrowser->append("数据库插入发送异常");
}
}
6.3、netcommunication.h
#ifndef NETCOMMUNICATION_H
#define NETCOMMUNICATION_H
#include <QObject>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonParseError>
#include <QtNetwork>
#include <QNetworkRequest>
#include <QUrl>
#include <QByteArray>
#include <QDebug>
#include <QDateTime>
#define false false
#define true true
extern bool NetWorkStatus;//网络
class NetCommunication : public QObject
{
Q_OBJECT
public:
explicit NetCommunication(QObject *parent = nullptr);
~NetCommunication(){}
QJsonDocument getData();
QByteArray getArray();
bool PostHttp(const QString address, const QByteArray &postStr, int s_time);
bool GetHttp(const QString address, int s_time);
bool PostHttpMultiPart(QString path, QByteArray fileData, QJsonDocument jd);
signals:
public slots:
private:
QNetworkAccessManager *accessManager;
QJsonDocument ServerReply;
QByteArray ServerArray;
QNetworkRequest *request;
bool replyFinished(QNetworkReply *reply);
};
#endif // NETCOMMUNICATION_H
6.4、netcommunication.c
#include "netcommunication.h"
#include <QApplication>
bool NetWorkStatus = false;//网络状态
NetCommunication::NetCommunication(QObject *parent) :
QObject(parent)
{
accessManager = new QNetworkAccessManager(this);
request = new QNetworkRequest();
}
bool NetCommunication::PostHttp(const QString address, const QByteArray &postStr, int s_time)
{
request->setUrl(QUrl(address));
request->setHeader(QNetworkRequest::ContentTypeHeader, "application/json ;encoding=utf-8");
QByteArray postData = postStr;
QNetworkReply *pReply = accessManager->post(*request, postData);
QEventLoop loop;
QTimer timer;
connect(accessManager, SIGNAL(finished(QNetworkReply*)), &loop, SLOT(quit()));
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
timer.start((s_time * 1000));//定时2s暂定
loop.exec();
if (timer.isActive()){
timer.stop();
}else{
return false;
}
return replyFinished(pReply);
}
bool NetCommunication::GetHttp(const QString address,int s_time)
{
request->setUrl(QUrl(address));
QNetworkReply *pReply = accessManager->get(*request);
request->setHeader(QNetworkRequest::ContentTypeHeader, "application/json ;encoding=utf-8");
QEventLoop loop;
QTimer timer;
connect(accessManager, SIGNAL(finished(QNetworkReply*)), &loop, SLOT(quit()));
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
timer.start((s_time * 1000));//定时2s暂定
loop.exec();
if (QNetworkReply::NoError == pReply->error()) {
ServerArray = pReply->readAll();
timer.stop();
pReply->deleteLater();
pReply = nullptr;
return true;
}
return false;
}
bool NetCommunication::PostHttpMultiPart(QString path,QByteArray fileData, QJsonDocument jd)
{
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
//此处boundary不能省! 需要和QHttpMultiPart中的一致
request->setHeader(QNetworkRequest::ContentTypeHeader,"multipart/form-data;boundary=boundary_.oOo._");
QHttpPart keyPart;
keyPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"key\""));
keyPart.setBody(path.toUtf8());
QHttpPart authorizationPart;
authorizationPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"Signature\""));
authorizationPart.setBody(jd["authorization"].toString().toUtf8());
QHttpPart tokenPart;
tokenPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"x-cos-security-token\""));
tokenPart.setBody(jd["token"].toString().toUtf8());
QHttpPart fileidPart;
fileidPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"x-cos-meta-fileid\""));
fileidPart.setBody(jd["cos_file_id"].toString().toUtf8());
QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data;name=\"file\""));
filePart.setBody(fileData);
multiPart->append(keyPart);
multiPart->append(authorizationPart);
multiPart->append(tokenPart);
multiPart->append(fileidPart);
multiPart->append(filePart);
request->setUrl(QUrl(jd["url"].toString()));
QNetworkReply *pReply = accessManager->post(*request, multiPart);
QEventLoop loop;
QTimer timer;
connect(accessManager, SIGNAL(finished(QNetworkReply*)), &loop, SLOT(quit()));
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
timer.start((10 * 1000));//定时2s暂定
loop.exec();
multiPart->deleteLater();
multiPart = nullptr;
if (timer.isActive()){
timer.stop();
}else{
return false;
}
return replyFinished(pReply);
}
bool NetCommunication::replyFinished(QNetworkReply *reply)
{
if (QNetworkReply::NoError == reply->error()) {
QTextCodec *codec = QTextCodec::codecForName("utf-8");
QByteArray ba = reply->readAll();
QString all = codec->toUnicode(ba);
QJsonDocument js = QJsonDocument::fromJson(all.toUtf8());
ServerReply = js;
if ("" != all){
NetWorkStatus = true;
}else{
NetWorkStatus = false;
qDebug() << "ba: " << ba;
}
}else{
NetWorkStatus = false;
}
reply->deleteLater();
reply = nullptr;
return NetWorkStatus;
}
QJsonDocument NetCommunication::getData()
{
return ServerReply;
}
QByteArray NetCommunication::getArray()
{
return ServerArray;
}
有需要的可以下载:
链接:https://pan.baidu.com/s/1yYIvyAXknPYgm0i088E-DA?pwd=8kwb
提取码:8kwb
7、qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization faile
7.1、先获取当前版本
输出当前QT支持的openSSL版本
qDebug()<<"QSslSocket="<<QSslSocket::sslLibraryBuildVersionString();
判断系统是否支持OpenSSL
如果此平台支持SSL,则返回true; 否则,返回false。 如果平台不支持SSL,则套接字将在连接阶段失败。不支持就需要看下面的解决方法。
qDebug() << "OpenSSL支持情况:" << QSslSocket::supportsSsl();
我的当前版本是
QSslSocket= "OpenSSL 1.1.1b 26 Feb 2019"
所以我等会下载的就会是OpenSSL v1.1.1o的版本
7.2、下载对应的版本
openssl下载地址:
http://slproweb.com/products/Win32OpenSSL.html
根据自己使用的QT编译器时32位还是64位,对应下载安装包。将下载的安装包进行安装,安装到第二个选项时,选择安装到指定的/bin目录下。安装之后,找到安装目录下的两个文件(其中我的是libcrypto-1_1.dll 和libssl-1_1.dll),拷贝到QT编译器目录下即可。
8、追加文件下载
今天在使用文件下载时,官方文档是格式是这样的
8.1、请求地址
POST https://api.weixin.qq.com/tcb/batchdownloadfile?access_token=ACCESS_TOKEN
8.2、请求参数
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
access_token / cloudbase_access_token | string | 是 | 接口调用凭证 | |
env | string | 是 | 云环境ID | |
file_list | Array.<Object> | 是 | 文件列表 |
8.2.1、file_list 的结构
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
fileid | string | 是 | 文件ID | |
max_age | number | 是 | 下载链接有效期 |
但是我发现加了 max_age那么一直提示错误代码:
-501007 | 云资源通用错误:参数错误 |
所以去除了之后一切正常,怀疑max_age是浏览器使用的吧
8.3、代码示例
QString url = "https://api.weixin.qq.com/tcb/batchdownloadfile?access_token="+Access_Token;
QJsonObject file{{"fileid","xxxxxxxxx/temp.json"}};//文件的fileid,前缀是固定值
QJsonArray array ;
array.append(file);
QJsonObject obj{
{"env","xxxxxxxxxx"},
{"file_list",array}/* 自定义文件路径 */
};
QByteArray oby = QJsonDocument(obj).toJson();
if (net_p->PostHttp(url,oby,3)) {
QJsonDocument jd = net_p->getData();
int errcode = jd["errcode"].toInt();
if(errcode == 0){
qDebug("文件上传链接返回成功");
QJsonObject download_obj = (jd["file_list"].toArray()).first().toObject();
QString download_url = download_obj["download_url"].toString();
qDebug() << "download_url: " << download_url;
if (net_p->GetHttp(download_url,10)) {
QFile file("D:/temp1.json");
file.open(QIODevice::WriteOnly);
file.write(net_p->getArray());
file.close();
qDebug() << ("下载文件完成");
messagebox(tr("提示"),tr("下载文件完成!!!"));
}else{
qDebug() << ("下载文件异常");
}
}else{
qDebug("文件下载链接返回异常");
}
}else{
qDebug("文件下载链接发送异常");
}