解码Qt HTTP+JSON实战(天气GET解析/百度AI POST封装)

JSON 数据格式与 Qt 操作

JSON 核心概念

JSON(JavaScript Object Notation)是轻量级数据交换格式,易读易解析,广泛用于本地存储和网络传输。核心是键值对

  • 键(key):必须是双引号包裹的字符串;
  • 值(value):支持字符串、数字、布尔值、null、JSON 对象、JSON 数组 6 种类型。

JSON 数据结构

  • JSON 对象{}包裹,内部是多个键值对(逗号分隔),例:

    json

    {"name":"张三","age":21,"isStudent":true}

  • JSON 数组[]包裹,内部是多个 JSON 值(逗号分隔),例:

    json

    [10, "hello", {"score":90}, [1,2,3]]

  • 嵌套关系:对象可嵌套对象 / 数组,数组也可嵌套对象 / 数组,支持复杂数据表示。

Qt 中 JSON 相关类(需 QT += core)

类名 作用
QJsonDocument 封装 JSON 文档,实现字节数组与 JSON 对象 / 数组的转换
QJsonObject 表示 JSON 对象,存储 / 读取键值对
QJsonArray 表示 JSON 数组,存储 / 遍历有序 JSON 值
QJsonValue 通用 JSON 值类型,可转换为字符串、数字、对象等具体类型

HTTP 协议与 Qt 网络编程

HTTP 核心特点

  • 基于 TCP 的请求 - 响应模型:客户端发请求,服务器返回响应;
  • 无状态:请求独立,服务器不记录历史(可通过 Cookie 补充状态);
  • 支持 GET/POST/PUT/DELETE 等请求方法;
  • 可扩展:通过请求 / 响应头传递元数据(如数据类型、编码)。

Qt HTTP 核心类(需 QT += network)

类名 作用
QNetworkAccessManager 管理网络请求,发送 GET/POST,处理请求生命周期
QNetworkRequest 封装请求信息(URL、请求头、超时时间)
QNetworkReply 封装响应信息(响应码、响应数据、错误信息)

HTTP与JSON的核心联动场景:

  • GET请求:从服务端获取JSON格式响应数据,解析后提取业务字段(如天气信息);
  • POST请求:将业务数据封装为JSON格式请求体,发送给服务端后,再解析JSON格式的响应结果。

HTTP GET请求(天气API):获取并解析JSON响应

接口基础信息

  • 接口地址:https://api.seniverse.com/v3/weather/now.json
  • 请求方式:GET
  • 核心参数:key(API密钥,必填)、location(查询位置,必填)、language(语言,默认zh-Hans)、unit(温度单位,默认c)
cpp 复制代码
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QUrlQuery>
#include <QEventLoop>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDebug>
#include <QTimer>  // QT5.14.2需用QTimer实现超时,替代QT5.15+的setTransferTimeout

/**
 * @brief 发送GET请求获取天气信息,并解析JSON格式响应数据
 * @param apiKey 心知天气API密钥(需自行在心知天气官网申请,必填项)
 * @param location 查询位置(支持城市名/拼音/经纬度,如"beijing"、"西雅图"、"39.93:116.40",必填)
 * @param language 响应数据的语言类型(默认zh-Hans简体中文,可选值:en英文/zh-Hant繁体中文等)
 * @param unit 温度单位(默认c表示摄氏度,可选f表示华氏度)
 * @return bool 解析成功返回true,解析/请求失败返回false
 * @note 1. QT5.14.2无setTransferTimeout()接口,使用QTimer+QEventLoop实现5秒超时控制
 *       2. QT5.14.2中直接使用QNetworkRequest::AcceptHeader枚举会报错,使用setRawHeader()设置请求头
 *       3. 同步请求使用QEventLoop实现,实际项目中建议改用异步信号槽(避免阻塞主线程)
 *			 不阻塞主线程(比如 GUI 程序不卡顿),通过信号槽监听请求的完成、失败、超时事件,这也是 QT 网络编程的推荐写法(同步仅适用于简单控制台程序)
 *       4. JSON解析前必须校验节点类型(如是否为对象/数组),防止接口返回格式异常导致崩溃
 */
bool getWeatherInfo(const QString &apiKey, const QString &location, 
                    const QString &language, const QString &unit)
{
    // 1. 构造带GET参数的URL(QUrlQuery自动处理中文/特殊字符的URL编码)
    QUrl url("https://api.seniverse.com/v3/weather/now.json"); // 心知天气实时天气接口地址
    QUrlQuery query;                                          // 用于拼接GET请求参数的工具类
    // 拼接必填参数:API密钥(心知天气控制台获取)
    query.addQueryItem("key", apiKey);          
    // 拼接必填参数:查询位置(支持城市名、拼音、经纬度等格式)
    query.addQueryItem("location", location);    
    // 拼接可选参数:响应语言(默认zh-Hans,无需可省略)
    query.addQueryItem("language", language);    
    // 拼接可选参数:温度单位(默认c,无需可省略)
    query.addQueryItem("unit", unit);           
    
    url.setQuery(query); // 将参数拼接到URL末尾,最终URL格式:xxx.json?key=xxx&location=xxx...

    // 2. 构造网络请求对象
    QNetworkRequest request(url); // 封装请求的URL、请求头等信息
    // 设置请求头,Accept: application/json 告诉服务端,客户端期望接收JSON格式的响应数据
    request.setRawHeader("Accept", "application/json");

    // 3. 初始化网络管理器并发送GET请求
    QNetworkAccessManager manager; // 管理网络请求的核心类,负责发送请求、接收响应
    /**
     * @brief QNetworkAccessManager::get 发送GET请求
     * @param request 封装好的请求对象(包含URL和请求头)
     * @return QNetworkReply* 响应对象,用于获取响应数据、错误信息、响应状态码等
     * @note QNetworkReply是一次性对象,使用后必须调用deleteLater()释放资源
     */
    QNetworkReply *reply = manager.get(request);

    // 4. 实现QT5.14.2的超时控制
    QEventLoop loop;       // 事件循环,用于阻塞等待请求完成(同步请求)
    QTimer timeoutTimer;   // 超时定时器,控制请求最大等待时间
    const int timeoutMs = 5000; // 超时时间设置为5秒
    timeoutTimer.setSingleShot(true); // 设置定时器为"单次触发"(触发一次后自动停止)
    timeoutTimer.start(timeoutMs);   // 启动定时器,5秒后触发timeout信号

    // 连接信号槽:请求完成(finished)时,退出事件循环
    QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    // 连接信号槽:定时器超时(timeout)时,也退出事件循环(处理超时场景)
    QObject::connect(&timeoutTimer, &QTimer::timeout, &loop, &QEventLoop::quit);
    
    loop.exec(); // 阻塞执行事件循环,直到请求完成或超时

    // 5. 校验请求结果(区分"正常完成"和"超时")
    bool isRequestSuccess = false; // 标记请求是否成功完成
    if (timeoutTimer.isActive()) {
        // 定时器仍在运行 → 未超时,请求正常完成
        timeoutTimer.stop(); // 手动停止定时器(避免定时器残留)
        
        // 检查请求是否发生错误(如网络断开、接口404等)
        if (reply->error() != QNetworkReply::NoError) {
            qDebug() << "[GET请求失败] 错误码:" << reply->error() << " | 错误信息:" << reply->errorString();
            reply->deleteLater(); // 释放QNetworkReply资源(必须调用,否则内存泄漏)
            return false;
        }
        isRequestSuccess = true; // 标记请求成功完成
    } else {
        // 定时器已停止 → 超时(5秒内未收到响应)
        qDebug() << "[GET请求超时] 超过" << timeoutMs << "ms未收到服务端响应";
        reply->abort(); // 终止未完成的请求(避免资源占用)
        reply->deleteLater(); // 释放资源
        return false;
    }

    // 6. 读取响应数据并解析JSON(仅请求成功时执行)
    if (isRequestSuccess) {
        // 读取服务端返回的所有字节数据(UTF-8编码)
        QByteArray responseData = reply->readAll();
        reply->deleteLater(); // 立即释放QNetworkReply资源

        /**
         * @brief QJsonDocument::fromJson 将JSON字节数据转换为JSON文档对象
         * @param json 待解析的JSON字节数组(服务端响应数据)
         * @param error 输出参数,存储解析错误信息(如JSON格式错误、编码错误)
         * @return QJsonDocument 成功返回有效文档对象,失败返回空文档
         */
        QJsonParseError parseError; // 存储JSON解析错误信息
        QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData, &parseError);
        
        // 校验JSON解析是否成功
        if (parseError.error != QJsonParseError::NoError) {
            qDebug() << "[JSON解析失败] 错误类型:" << parseError.error 
                     << " | 错误信息:" << parseError.errorString();
            return false;
        }

        // 7. 逐层解析JSON数据(严格校验节点类型,避免崩溃)
        // 第一步:校验根节点是否为JSON对象(接口返回根节点是{})
        if (!jsonDoc.isObject()) {
            qDebug() << "[JSON格式错误] 根节点非对象类型,响应数据:" << responseData;
            return false;
        }
        QJsonObject rootObj = jsonDoc.object(); // 提取根对象

        // 第二步:提取"results"字段(接口返回是数组类型)
        QJsonValue resultsValue = rootObj.value("results"); // 根据键获取值(通用类型QJsonValue)
        if (!resultsValue.isArray()) { // 校验是否为数组类型
            qDebug() << "[JSON格式错误] results字段非数组类型,实际类型:" << resultsValue.type();
            return false;
        }
        QJsonArray resultsArray = resultsValue.toArray(); // 转换为JSON数组
        if (resultsArray.isEmpty()) { // 校验数组是否为空(无天气数据)
            qDebug() << "[JSON数据异常] results数组为空,无天气信息";
            return false;
        }

        // 第三步:解析第一个天气结果(单个location对应一个结果)
        QJsonObject resultObj = resultsArray.at(0).toObject(); // 提取数组第一个元素(转换为对象)

        // 解析位置信息(嵌套对象)
        QJsonObject locationObj = resultObj.value("location").toObject();
        QString locationName = locationObj.value("name").toString();       // 城市名称(如"北京")
        QString locationCountry = locationObj.value("country").toString(); // 国家(如"CN")
        QString locationTimezone = locationObj.value("timezone").toString(); // 时区(如"Asia/Shanghai")
        qDebug() << "==================== 位置信息 ====================";
        qDebug() << "城市名称:" << locationName 
                 << " | 国家:" << locationCountry 
                 << " | 时区:" << locationTimezone;

        // 解析实时天气信息(嵌套对象)
        QJsonObject nowObj = resultObj.value("now").toObject();
        QString weatherText = nowObj.value("text").toString();         // 天气现象文字(如"多云")
        QString temperature = nowObj.value("temperature").toString(); // 实时温度(单位:℃/℉)
        QString humidity = nowObj.value("humidity").toString();       // 相对湿度(百分比)
        QString windDirection = nowObj.value("wind_direction").toString(); // 风向(如"西北风")
        qDebug() << "==================== 实时天气 ====================";
        qDebug() << "天气状况:" << weatherText 
                 << " | 实时温度:" << temperature << "℃"
                 << " | 相对湿度:" << humidity << "%"
                 << " | 风向:" << windDirection;

        // 解析数据更新时间
        QString lastUpdate = resultObj.value("last_update").toString();
        qDebug() << "==================== 数据更新 ====================";
        qDebug() << "最后更新时间(本地时间):" << lastUpdate;

        return true; // 解析成功
    }

    return false;
}

// 重载函数:提供默认参数(简化调用)
/**
 * @brief 重载getWeatherInfo,提供默认参数(语言+单位)
 * @param apiKey 心知天气API密钥
 * @param location 查询位置
 * @return bool 解析成功返回true,失败返回false
 */
bool getWeatherInfo(const QString &apiKey, const QString &location)
{
    return getWeatherInfo(apiKey, location, "zh-Hans", "c");
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv); // QT核心应用程序对象(必须创建)

    // 调用示例:替换为你自己的心知天气API Key
    QString apiKey = "your_api_key_here"; // 心知天气API密钥(需自行申请)
    getWeatherInfo(apiKey, "beijing");    // 查询北京的实时天气

    return a.exec(); // 启动QT事件循环
}

HTTP POST请求(百度图像识别):封装JSON请求体并解析响应

接口前置说明

  • 鉴权要求:调用百度接口需先获取Access_token(有效期30天),请求时携带在URL中;
  • 请求格式:POST请求体为JSON格式,Content-Type需设置为application/json;charset=utf-8

步骤1:获取Access_token(鉴权)

功能说明

通过API Key和Secret Key向百度鉴权接口发送POST请求获取,参数通过URL拼接传递,响应为JSON格式。

cpp 复制代码
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QUrlQuery>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile>
#include <QByteArray>
#include <QDebug>
#include <QSettings>
#include <QEventLoop>
#include <QJsonParseError>

/**
 * @brief 获取百度接口的Access_token(鉴权令牌)
 * @details 百度开放平台基于OAuth2.0授权,所有API调用前必须获取此令牌,有效期30天(2592000秒)
 * @param apiKey 必须参数,百度控制台创建应用后生成的API Key(client_id)
 * @param secretKey 必须参数,百度控制台创建应用后生成的Secret Key(client_secret)
 * @return QString 成功返回有效Access_token字符串,失败返回空字符串
 * @note 1. 项目.pro必须添加QT += core network,否则缺失QNetwork相关类
 *       2. 错误场景:API Key/Secret Key错误返回"invalid client"、网络超时/异常返回空
 *       3. 建议缓存token到本地(如QSettings),避免重复请求
 *       4. 请求方式:POST,参数通过URL拼接传递,Content-Type为application/x-www-form-urlencoded
 */
QString getBaiduAIAccessToken(const QString &apiKey, const QString &secretKey)
{
    // 1. 构建鉴权请求URL
    QUrl url("https://aip.baidubce.com/oauth/2.0/token");
    QUrlQuery queryParams;
    queryParams.addQueryItem("grant_type", "client_credentials"); // 固定值,标识客户端凭证
    queryParams.addQueryItem("client_id", apiKey);                // 对应API Key
    queryParams.addQueryItem("client_secret", secretKey);         // 对应Secret Key
    url.setQuery(queryParams);

    // 2. 初始化网络请求
    QNetworkAccessManager netManager;
    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json;charset=utf-8");

    // 3. 发送同步POST请求(阻塞等待响应,适合简单场景)
    QNetworkReply *reply = netManager.post(request, QByteArray());
    QEventLoop eventLoop;
    QObject::connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
    eventLoop.exec(QEventLoop::ExcludeUserInputEvents); // 排除用户输入事件

    // 4. 解析响应
    QString accessToken;
    if (reply->error() == QNetworkReply::NoError) {
        QByteArray responseData = reply->readAll();
        QJsonParseError jsonErr;
        QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData, &jsonErr);
        
        if (jsonErr.error == QJsonParseError::NoError && jsonDoc.isObject()) {
            QJsonObject jsonObj = jsonDoc.object();
            // 成功响应核心字段:access_token(令牌)、expires_in(有效期,秒)
            if (jsonObj.contains("access_token")) {
                accessToken = jsonObj["access_token"].toString();
                qDebug() << "[鉴权成功] Access_token:" << accessToken.left(20) << "...";
                qDebug() << "[鉴权成功] 有效期:" << jsonObj["expires_in"].toInt() <<"秒";
            } else {
                qDebug() << "[鉴权失败] 响应无access_token字段,内容:" << responseData;
            }
        } else {
            qDebug() << "[鉴权失败] JSON解析错误:" << jsonErr.errorString();
        }
    } else {
        qDebug() << "[鉴权失败] 网络错误:" << reply->errorString() << "(错误码:" << reply->error() << ")";
    }

    reply->deleteLater(); // 释放资源
    return accessToken;
}

注意事项

  • 需在百度控制台(https://ai.baidu.com/)创建应用,获取API Key和Secret Key;
  • 项目.pro文件必须添加 QT += core network,否则缺失网络相关类;
  • 若提示"invalid client"错误,检查API Key/Secret Key是否填写正确;
  • Token建议缓存到本地(如QSettings或文本文件),避免每次启动都请求。

步骤2:图像识别(JSON请求体+POST)

功能说明

基于步骤1获取的Access_token,将本地图片转换为Base64编码,封装成JSON格式请求体,发送POST请求调用百度图像识别接口,解析返回的识别结果。

cpp 复制代码
/**
 * @brief 将本地图片转换为百度接口要求的Base64编码
 * @details 要求:Base64编码后去掉头(如data:image/jpeg;base64,)、大小≤4M、支持jpg/png/bmp、最短边≥15px、最长边≤4096px
 * @param imagePath 必须参数,本地图片路径(绝对/相对路径,支持中文)
 * @return QString 成功返回纯Base64字符串(无编码头),失败返回空字符串
 * @note 1. 路径含中文时需确保项目编码为UTF-8(.pro添加CONFIG += utf8_source)
 *       2. 失败场景:路径错误、文件不存在、文件损坏、大小超过4M
 */
QString imageToBase64(const QString &imagePath)
{
    // 1. 打开图片文件(支持中文路径)
    QFile imageFile(imagePath);
    if (!imageFile.open(QIODevice::ReadOnly)) {
        qDebug() << "[Base64编码失败] 图片文件打开失败,路径:" << imagePath;
        return "";
    }

    // 2. 读取文件并检查大小(4M = 4*1024*1024 = 4194304 字节)
    QByteArray imageData = imageFile.readAll();
    imageFile.close();
    const int MAX_IMAGE_SIZE = 4 * 1024 * 1024;
    if (imageData.size() > MAX_IMAGE_SIZE) {
        qDebug() << "[Base64编码失败] 图片超过4M限制,当前大小:" << imageData.size() << "字节";
        return "";
    }

    // 3. 转换为Base64编码(无编码头)
    return imageData.toBase64();
}

/**
 * @brief 调用百度动物识别接口
 * @details 封装form-urlencoded请求体发送POST请求,解析返回的识别结果(含名称、置信度、百科信息)
 * @param accessToken 必须参数,有效的Access_token
 * @param imagePath 必须参数,本地图片路径
 * @param topNum 可选参数,返回置信度TOP N的结果,默认6
 * @param baikeNum 可选参数,返回百科信息的数量,0=不返回,默认0
 * @return QJsonObject 成功返回识别结果JSON对象,失败返回空对象
 * @note 1. 请求Content-Type必须为application/x-www-form-urlencoded(百度接口要求)
 *       2. image参数需Base64编码后再URLencode,否则接口无法解析
 *       3. 错误码216101:image参数缺失/为空/格式错误;216102:识别场景填写错误
 *       4. 接口URL替换可切换识别场景:植物识别=rest/2.0/image-classify/v1/plant
 */
QJsonObject baiduAnimalClassify(const QString &accessToken, const QString &imagePath, int topNum = 6, int baikeNum = 0)
{
    // 1. 参数校验
    if (accessToken.isEmpty()) {
        qDebug() << "[识别失败] Access_token为空";
        return QJsonObject();
    }
    QString base64Image = imageToBase64(imagePath);
    if (base64Image.isEmpty()) {
        qDebug() << "[识别失败] 图片Base64编码失败";
        return QJsonObject();
    }

    // 2. 关键:Base64字符串需URL编码(百度接口强制要求)
    QString urlEncodedImage = QUrl::toPercentEncoding(base64Image);
    qDebug() << "[识别准备] Base64编码后长度:" << base64Image.size() << ",URL编码后长度:" << urlEncodedImage.size();

    // 3. 构建识别请求URL(拼接access_token)
    QUrl url("https://aip.baidubce.com/rest/2.0/image-classify/v1/animal");
    QUrlQuery queryParams;
    queryParams.addQueryItem("access_token", accessToken);
    url.setQuery(queryParams);

    // 4. 构建form-urlencoded请求体
    QUrlQuery formParams;
    formParams.addQueryItem("image", urlEncodedImage);    // 核心参数:URL编码后的Base64字符串
    formParams.addQueryItem("top_num", QString::number(topNum)); // 返回TOP N结果
    formParams.addQueryItem("baike_num", QString::number(baikeNum)); // 百科信息数量
    QByteArray requestData = formParams.toString(QUrl::FullyEncoded).toUtf8();

    // 5. 发送POST请求(Content-Type改为application/x-www-form-urlencoded)
    QNetworkAccessManager netManager;
    QNetworkRequest request(url);
    // 核心修正:设置正确的Content-Type
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded;charset=utf-8");

    QNetworkReply *reply = netManager.post(request, requestData);
    
    QEventLoop eventLoop;
    QTimer timeoutTimer;
    const int timeoutMs = 10000;
    timeoutTimer.setSingleShot(true); // 单次触发
    timeoutTimer.setInterval(timeoutMs); // 设置超时时间(默认10秒)

    // 连接信号:请求完成/超时都退出事件循环
    QObject::connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
    QObject::connect(&timeoutTimer, &QTimer::timeout, [&]() {
        qDebug() << "[识别失败] 请求超时(超过" << timeoutMs << "毫秒)";
        reply->abort(); // 超时终止请求
        eventLoop.quit();
    });

    // 启动定时器+事件循环
    timeoutTimer.start();
    eventLoop.exec(QEventLoop::ExcludeUserInputEvents);

    // 6. 解析响应
    QJsonObject resultJson;
    if (reply->error() == QNetworkReply::NoError) {
        QByteArray responseData = reply->readAll();
        // qDebug() << "[识别响应] 原始返回数据:" << responseData; // 打印原始响应,方便排查

        QJsonParseError jsonErr;
        QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData, &jsonErr);
        
        if (jsonErr.error == QJsonParseError::NoError && jsonDoc.isObject()) {
            resultJson = jsonDoc.object();
            // 检查接口返回的错误码
            if (resultJson.contains("error_code")) {
                int errCode = resultJson["error_code"].toInt();
                QString errMsg = resultJson["error_msg"].toString();
                qDebug() << "[识别失败] 接口错误:" << errCode << "-" << errMsg;
                resultJson = QJsonObject(); // 置空表示失败
            } else {
                qDebug() << "[识别成功] 响应解析完成";
            }
        } else {
            qDebug() << "[识别失败] JSON解析错误:" << jsonErr.errorString();
        }
    } else {
        qDebug() << "[识别失败] 网络错误:" << reply->errorString() << "(错误码:" << reply->error() << ")";
    }

    reply->deleteLater();
    return resultJson;
}

/**
 * @brief 缓存Access_token到本地配置文件(QSettings)
 * @param token 要缓存的Access_token
 * @param key 缓存键名,默认"baidu_ai_access_token"
 * @note 缓存路径:Windows=%APPDATA%/MyCompany/BaiduAIImageClassify.ini;Linux=~/.config/MyCompany/BaiduAIImageClassify.conf
 */
void cacheAccessToken(const QString &token, const QString &key = "baidu_ai_access_token")
{
    QSettings settings("MyCompany", "BaiduAIImageClassify");
    settings.setValue(key, token);
    settings.sync(); // 立即写入磁盘
    qDebug() << "[缓存成功] Access_token已保存到本地";
}

/**
 * @brief 读取本地缓存的Access_token
 * @param key 缓存键名,默认"baidu_ai_access_token"
 * @return QString 有缓存返回token,无缓存返回空
 */
QString getCachedAccessToken(const QString &key = "baidu_ai_access_token")
{
    QSettings settings("MyCompany", "BaiduAIImageClassify");
    QString token = settings.value(key).toString();
    if (token.isEmpty()) {
        qDebug() << "[缓存读取] 无本地缓存的Access_token";
    } else {
        qDebug() << "[缓存读取] 成功读取本地Access_token";
    }
    return token;
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // ===================== 配置项(替换为你的实际参数)=====================
    const QString API_KEY = "你的百度应用API Key";       // 替换为自己的API Key
    const QString SECRET_KEY = "你的百度应用Secret Key"; // 替换为自己的Secret Key
    const QString IMAGE_PATH = "C:/test/animal.jpg";       // 替换为本地图片路径
    const int TOP_NUM = 5;                                 // 返回TOP 5识别结果
    const int BAIKE_NUM = 2;                               // 返回2条百科信息
    // =====================================================================

    // 1. 获取Access_token(优先读缓存,无缓存则请求)
    QString accessToken = getCachedAccessToken();
    if (accessToken.isEmpty()) {
        accessToken = getBaiduAIAccessToken(API_KEY, SECRET_KEY);
        if (accessToken.isEmpty()) {
            qDebug() << "[程序退出] 无法获取Access_token";
            return -1;
        }
        cacheAccessToken(accessToken); // 缓存token
    }

    // 2. 调用动物识别接口
    QJsonObject classifyResult = baiduAnimalClassify(accessToken, IMAGE_PATH, TOP_NUM, BAIKE_NUM);
    if (classifyResult.isEmpty()) {
        qDebug() << "[程序退出] 图像识别失败";
        return -1;
    }

    // 3. 解析并输出识别结果
    qDebug() << "\n==================== 识别结果 ====================";
    // log_id:唯一日志ID,用于百度技术支持定位问题
    qulonglong logId = classifyResult["log_id"].toVariant().toULongLong();
    qDebug() << "日志ID(log_id):" << logId;

    // 解析识别结果数组
    QJsonArray resultArray = classifyResult["result"].toArray();
    for (int i = 0; i < resultArray.size(); ++i) {
        QJsonObject item = resultArray[i].toObject();
        QString name = item["name"].toString();       // 动物名称
        QString score = item["score"].toString();     // 置信度(0-1,越高越准确)
        
        qDebug() << QString("\n第%1个结果:").arg(i+1);
        qDebug() << "  名称:" << name;
        qDebug() << "  置信度:" << score;

        // 解析百科信息(可选)
        if (item.contains("baike_info") && !item["baike_info"].isNull()) {
            QJsonObject baikeInfo = item["baike_info"].toObject();
            QString baikeUrl = baikeInfo["baike_url"].toString();       // 百科链接
            QString description = baikeInfo["description"].toString(); // 百科描述
            qDebug() << "  百科链接:" << baikeUrl;
            qDebug() << "  百科简介:" << description.left(100) << "..."; // 截断过长的描述
        }
    }
    qDebug() << "\n==================== 解析完成 ====================";

    return a.exec();
}

注意事项

  • 图片路径需填写正确(相对路径/绝对路径均可),若路径含中文,确保QT项目编码为UTF-8;
  • 识别场景(scenes)需填写百度支持的类型(如animal/plant/currency等),填错会返回216102错误;
  • 若返回"download image error",检查imgUrl(若使用)是否可访问,或图片Base64编码是否正确;
  • 项目.pro文件需添加 QT += core network,并确保引入所有头文件。

核心总结

  • 步骤1核心:通过API Key/Secret Key获取Access_token,参数拼URL、请求体为空,超时5秒,重点是Token提取和缓存;
  • 步骤2核心:图片转Base64→封装JSON请求体→设置Content-Type→发送POST→解析识别结果,超时10秒,重点是JSON格式和业务错误校验;
  • QT5.14.2适配关键:用setRawHeader替代请求头枚举、用QTimer+QEventLoop替代setTransferTimeout

核心总结

  • GET请求:参数通过QUrlQuery拼接到URL,JSON响应需逐层解析(根对象→数组→子对象),解析前必须校验节点类型;
  • POST请求:鉴权Token需提前获取,JSON请求体需用QJsonObject/QJsonArray封装,Content-Type必须匹配接口要求;
  • 通用规范:网络请求需设置超时,QNetworkReply必须调用deleteLater()释放资源,JSON解析需处理格式错误场景。
相关推荐
喵个咪3 天前
Go-Wind HTTP 服务器从入门到精通
后端·http·go
用户805533698034 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner4 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Goodbye9 天前
大模型无状态架构:从 HTTP 协议到 Harness AI 工程的深度解析
http
Quz9 天前
QML Hello World 入门示例
qt
xcyxiner12 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner13 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner13 天前
DicomViewer (添加模型类)3
qt
xcyxiner14 天前
DicomViewer (目录调整) 2
qt
xcyxiner14 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt