json存二进制数据必须先base64编码,因json仅支持utf-8字符串,原始字节含\0或\xff会导致截断或乱码;正确流程是二进制→base64字符串→json字段,且需注意输入接口、文件读取模式、内存优化及解码前清洗校验。

JSON里存二进制数据必须先Base64编码
直接把std::vector<uint8_t></uint8_t>或原始图片字节塞进nlohmann::json会触发隐式转换,结果是乱码或截断——JSON标准只认UTF-8字符串,二进制里的\0、\xFF会被当成字符串终止或非法字符。正确做法永远是:二进制 → Base64字符串 → JSON字段。
- 典型场景:用
cv::imencode生成std::vector<uint8_t></uint8_t>图片数据,再塞进{"image": "..."}字段 - 别用
json["image"] = bytes(bytes是std::vector<uint8_t></uint8_t>),nlohmann会尝试按char解释,遇到\0就停 - 必须显式调用Base64编码函数,输出
std::string,再赋值:json["image"] = base64_encode(bytes);
输入必须用const unsigned char*或std::string_view,不能传std::string含\0
很多同学把图片数据先存成std::string再传给Base64函数,结果编码一半就断了——因为std::string::c_str()在第一个\0处终止,而PNG/JPEG头部就有\0。查表法Base64函数的输入接口必须避开这个陷阱。
- 推荐签名:
std::string base64_encode(std::string_view in)或std::string base64_encode(const unsigned char* data, size_t len) - 从OpenCV来:
base64_encode(std::string_view{reinterpret_cast<const char>(img_data.data()), img_data.size()})</const> - 从文件读取时,务必用
std::ios::binary模式打开,否则Windows下\r\n被转义,解码后图片损坏
高频调用时避免重复构造std::string,用输出迭代器
如果每秒要编码上百张小图(比如监控帧),每次调用base64_encode都new一个std::string,堆分配开销会吃掉30%以上CPU。尤其处理1–2KB图片时,std::string内部小字符串优化(SSO)也救不了——多数实现SSO阈值是15–22字节,远小于Base64输出长度。
- 改用迭代器接口:
void base64_encode(std::string_view in, std::back_insert_iterator<:string> out)</:string> - 调用:
std::string dst; dst.reserve((in.size() + 2) / 3 * 4); base64_encode(in, std::back_inserter(dst)); - reserve能避免多次realloc;
std::back_inserter直接往已有buffer末尾写,零额外分配
解码后写文件或送OpenCV,必须检查=补位和非法字符
JSON字段里的Base64字符串可能被人工编辑过,或HTTP传输中被URL decode污染(比如+变空格、/变%2F),直接解码会崩溃或返回错误长度。nlohmann/json默认不做校验,得自己兜底。
- 解码前先做清洗:
std::replace(s.begin(), s.end(), ' ', '+'); std::replace(s.begin(), s.end(), '_', '/');(适配URL-safe变种) - 检查长度是否为4的倍数,不是就补
=直到满足;再验证每个字符是否在ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/中 - 解码输出建议用
std::vector<uint8_t>&</uint8_t>引用接收,避免中间拷贝;送cv::imdecode前确认data非空且size > 0
最易忽略的是:Base64字符串末尾的=不是可选修饰,而是长度对齐的强制要求。少一个=,解码出来的字节数就少1–2个,OpenCV加载图片时静默失败,只返回空cv::Mat。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











