从Cpp经Redis PubSub发送编码CV图像至Rust时遇无效UTF-8错误
问题:Redis Pub/Sub传输编码图像时Rust接收报错Invalid UTF-8
C++发送端代码
cv::Mat output_image; output_image = cv::Mat(pic_height, pic_width * 2, CV_8UC3); cv::imwrite(filename, output_image); std::vector<uchar> encoded_image; cv::imencode(".png", output_image, encoded_image); std::string image_str(encoded_image.begin(), encoded_image.end()); string image_msg; image_msg = to_string(pic_id) + "~" + metadata + "~" + image_str; RedisCommunicator::GetInstance()->Publish(image_channel, image_msg);
Rust接收端代码
let mut redis_pubsub = redis_connection.as_pubsub(); redis_pubsub.subscribe(image_channel).expect("Failed to subscribe to channel"); info!("subscribed to channel: {}", image_channel); redis_pubsub.set_read_timeout(Some(Duration::from_secs(5))).expect("Failed to set read timeout"); for _ in 0..3 { info!("###### Waiting for message..."); let log = match redis_pubsub.get_message(){ Ok(T) => T, Err(e) => {info!("Failed to get message: {}", e); continue;}, }; let payload : String = match log.get_payload() { Ok(T) => T, Err(e) => {info!("Failed to get payload: {}", e);continue;}, }; if log_enabled!(Level::Debug) { debug!("Got log from channel: '{}', log: {}", log.get_channel_name(), payload); } info!("{{\"{}\": {}}}", image_channel, payload); sleep(Duration::from_secs(1)); };
错误日志
[rusty_record] subscribed to channel: image_sender [rusty_record] ###### Waiting for message... [rusty_record] Failed to get payload: Invalid UTF-8- TypeError [rusty_record] ###### Waiting for message... [rusty_record] Failed to get payload: Invalid UTF-8- TypeError [rusty_record] ###### Waiting for message... [rusty_record] Failed to get payload: Invalid UTF-8- TypeError
Redis-cli查看的消息内容
redis-cli subscribe image_sender 1) "message" 2) "image_sender" 3) "3707~{\"pic_gain\":0,\"pic_exposure\":0,\"pic_time\":0,\"camera_id\":0,\"latitude\":0.000000,\"longitude\":0.000000,\"altitude\":0.000000,\"heading_degrees\":0.000000,\"auto_white_balance\":true}~\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00C\x00\x02\x01\x01\x01\x01\x01\x02\x01\x01\x01\x02\x02\x02\x02\x02\x04\x03\x02\x02\x02\x02\x05\x04\x04\x03\x04\x06\x05\x06\x06\x06\x05\x06\x06\x06\a\t\b\x06\a\t\a\x06\x06\b\x0b\b\t\n\n\n\n\n\x06\b\x0b\x0c\x0b\n\x0c\t\n\n\n\xff\xdb\x00C\x01\x02\x02\x02\x02\x02\x02\x05\x03\x03\x05\n\a\x06\a\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\xff\xc0\x00\x11\b\x00\xc0\x05\xa0\x03\x01\"\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04\x00\x00\x01}\x01\x02\x03\x00\x04\x11\x05\x12!1A\x06\x13Qa\a\"q\x142\x81\x91\xa1\b#B\xb1\xc1\x15R\xd1\xf0$3br\x82\t\n\x16\x17\x18\x19\x1a%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\...
普通字符串测试正常
C++测试代码
string image_msg; image_msg = to_string(pic_id) + "~" + metadata + "~" + "image_str"; RedisCommunicator::GetInstance()->Publish(image_channel, image_msg);
Rust接收日志
[rusty_record] subscribed to channel: image_sender [rusty_record] ###### Waiting for message... [rusty_record] Got log from channel: 'image_handler_0', log: 7812~{"pic_gain":0,"pic_exposure":0,"pic_time":0,"camera_id":0,"latitude":0.000000,"longitude":0.000000,"altitude":0.000000,"heading_degrees":0.000000,"auto_white_balance":true}~image_str [rusty_record] {"image_sender": 7812~{"pic_gain":0,"pic_exposure":0,"pic_time":0,"camera_id":0,"latitude":0.000000,"longitude":0.000000,"altitude":0.000000,"heading_degrees":0.000000,"auto_white_balance":true}~image_str} [rusty_record] ###### Waiting for message... [rusty_record] Got log from channel: 'image_handler_0', log: 7813~{"pic_gain":0,"pic_exposure":0,"pic_time":0,"camera_id":0,"latitude":0.000000,"longitude":0.000000,"altitude":0.000000,"heading_degrees":0.000000,"auto_white_balance":true}~image_str [rusty_record] {"image_sender": 7813~{"pic_gain":0,"pic_exposure":0,"pic_time":0,"camera_id":0,"latitude":0.000000,"longitude":0.000000,"altitude":0.000000,"heading_degrees":0.000000,"auto_white_balance":true}~image_str} [rusty_record] ###### Waiting for message... [rusty_record] Got log from channel: 'image_handler_0', log: 7814~{"pic_gain":0,"pic_exposure":0,"pic_time":0,"camera_id":0,"latitude":0.000000,"longitude":0.000000,"altitude":0.000000,"heading_degrees":0.000000,"auto_white_balance":true}~image_str [rusty_record] {"image_sender": 7814~{"pic_gain":0,"pic_exposure":0,"pic_time":0,"camera_id":0,"latitude":0.000000,"longitude":0.000000,"altitude":0.000000,"heading_degrees":0.000000,"auto_white_balance":true}~image_str}
解决方案
核心问题:编码后的图像是二进制数据,包含大量非UTF-8字符,Rust端直接解析为String(要求严格UTF-8)导致解码失败。
方案一:直接处理二进制payload
修改Rust接收代码,用Vec<u8>接收二进制数据,再拆分各部分:
// 替换原有的String类型,改用Vec<u8>接收二进制数据 let payload: Vec<u8> = match log.get_payload() { Ok(payload) => payload, Err(e) => { info!("Failed to get payload: {}", e); continue; } }; // 按~拆分二进制数据 let parts: Vec<&[u8]> = payload.split(|&b| b == b'~').collect(); if parts.len() != 3 { info!("Invalid message format"); continue; } // 解析各部分:pic_id和metadata转为字符串,图像数据保留二进制 let pic_id = String::from_utf8_lossy(parts[0]); let metadata = String::from_utf8_lossy(parts[1]); let image_data = parts[2]; // 保存图像到文件 std::fs::write("received_image.png", image_data).expect("Failed to write image file"); info!("Received image: pic_id={}, metadata={}", pic_id, metadata);
方案二:C++端对图像做Base64编码(更可靠)
避免分隔符冲突,将二进制图像转为UTF-8兼容的Base64字符串后发送:
C++端修改代码
#include <base64.h> // 引入Base64编码库,如libb64 std::vector<uchar> encoded_image; cv::imencode(".png", output_image, encoded_image); // 二进制图像转Base64字符串 std::string image_base64 = base64_encode(encoded_image.data(), encoded_image.size()); std::string image_msg = to_string(pic_id) + "~" + metadata + "~" + image_base64; RedisCommunicator::GetInstance()->Publish(image_channel, image_msg);
Rust端解码处理
use base64; // 在Cargo.toml添加base64依赖 // 保留原有的String接收逻辑 let payload: String = match log.get_payload() { Ok(payload) => payload, Err(e) => { info!("Failed to get payload: {}", e); continue; } }; let parts: Vec<&str> = payload.split('~').collect(); if parts.len() != 3 { info!("Invalid message format"); continue; } let pic_id = parts[0]; let metadata = parts[1]; let image_base64 = parts[2]; // Base64解码为二进制图像数据 let image_data = base64::decode(image_base64).expect("Failed to decode base64 image"); std::fs::write("received_image.png", image_data).expect("Failed to write image file"); info!("Received image: pic_id={}, metadata={}", pic_id, metadata);
内容的提问来源于stack exchange,提问作者lior.i
相关产品推荐
相关产品推荐

