Qt中如何拆分含多JPG的QByteArray并还原单张图片?
多图片Blob存储与拆分问题的解决方案
问题根源
你用$作为分隔符合并图片字节的思路根本行不通——JPG这类二进制文件的字节流里完全可能包含$字符,拆分时自然会把图片内容里的$当成分隔符,导致拆分出一堆无效数据。
方案一:彻底修改存储方式(首推)
这是从根源解决问题的办法,分两种实现思路:
1. 每张图片单独存一条记录
直接在SQLite表里给每张图片单独建一行,比如表结构可以设为:
CREATE TABLE images ( id INTEGER PRIMARY KEY AUTOINCREMENT, image_blob BLOB NOT NULL, -- 其他字段:比如图片名称、上传时间等 );
存储时循环处理用户选中的每张图片,把单张图片的QByteArray直接作为image_blob字段插入数据库;读取时直接查询对应行的image_blob,拿到的就是完整的单张图片字节流,完全不需要拆分,这是最稳妥、最易维护的方式。
2. 用长度前缀法合并存储(必须合并到同一Blob时用)
如果业务上必须把多张图片存在同一个Blob字段里,别用字符分隔,改用固定长度的长度前缀:存储每张图片前,先写入该图片的字节长度(比如用4字节无符号整数,统一字节序),再写入图片本身的字节数据,所有图片依次叠加。
存储合并代码示例:
QByteArray combineImages(const QList<QByteArray>& imageList) { QByteArray combinedData; QDataStream stream(&combinedData, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); // 统一小端字节序,避免跨平台问题 for (const auto& imgData : imageList) { quint32 imgLen = static_cast<quint32>(imgData.size()); stream << imgLen; // 写入图片长度 stream.writeRawData(imgData.constData(), imgLen); // 写入图片字节 } return combinedData; }
拆分还原代码示例:
QList<QByteArray> splitImages(const QByteArray& combinedData) { QList<QByteArray> imageList; QDataStream stream(combinedData); stream.setByteOrder(QDataStream::LittleEndian); while (!stream.atEnd()) { quint32 imgLen; stream >> imgLen; // 检查数据流是否正常 if (stream.status() != QDataStream::Ok) { break; } QByteArray imgData; imgData.resize(imgLen); stream.readRawData(imgData.data(), imgLen); // 确保读取到完整的图片数据 if (imgData.size() == imgLen) { imageList.append(imgData); } } return imageList; }
方案二:临时处理已用$存储的旧数据(仅救急)
如果已经有一批用$分隔的旧数据需要导出,只能针对JPG格式做临时处理——利用JPG的结束标记0xFFD9来辅助判断:遍历字节流,每次找到0xFFD9后,检查后面是否紧跟$,如果是,就把从上次起始位置到0xFFD9的部分作为一张图片,跳过$继续找下一个。
注意:这种方法只适用于JPG,而且容错率极低(比如图片内容里意外出现0xFFD9就会误判),只能用来导出旧数据,之后必须换成方案一的存储方式。
代码示例:
QList<QByteArray> splitJpgFromDollarSeparated(const QByteArray& combinedData) { QList<QByteArray> imageList; int startPos = 0; const char* dataPtr = combinedData.constData(); int totalLen = combinedData.size(); for (int i = 0; i < totalLen - 1; ++i) { // 匹配JPG结束标记 0xFF 0xD9 if (dataPtr[i] == 0xFF && dataPtr[i+1] == 0xD9) { // 检查下一个字符是否是分隔符$ if (i + 2 < totalLen && dataPtr[i+2] == '$') { imageList.append(combinedData.mid(startPos, i + 2 - startPos)); startPos = i + 3; // 跳过$,从下一个字节开始 i += 2; // 跳过已处理的结束标记和分隔符 } } } // 处理最后一张没有结尾$的图片 if (startPos < totalLen) { imageList.append(combinedData.mid(startPos)); } return imageList; }
内容的提问来源于stack exchange,提问作者mar777
相关产品推荐
相关产品推荐

