问题描述
Qt/C++ 程序有一个函数将对象数据 (_token
) 写入文件,如下所示:
QFile f( _tokenFile);
if (!f.open( QIODevice::WriteOnly)) {
qDebug() << "Unable to open token file for writing" << f.errorString();
return false;
}
QByteArray tokenBa;
QDataStream ds( &tokenBa,QIODevice::WriteOnly);
ds << _token;
tokenBa = qCompress( tokenBa);
f.write( tokenBa);
f.close();
_token 是以下 struct
的实例:
struct Token {
QString accessToken;
QString refreshToken;
QString clientSecret;
QString authCode;
QTime expiryTime;
enum AuthState {
A_Invalid,A_RequestAuth,A_Authenticated,};
AuthState state;
Token() : state( A_Invalid) {}
bool isValid() const {
if (accessToken.isEmpty() ||
refreshToken.isEmpty()) {
return false;
}
return true;
}
void inValidate() {
accessToken.clear();
refreshToken.clear();
clientSecret.clear();
authCode.clear();
expiryTime = QTime();
}
void cleanUp() {
accessToken.clear();
refreshToken.clear();
clientSecret.clear();
authCode.clear();
expiryTime = QTime();
}
};
当文件被保存时,它在开始处有 4 个额外的字节,将文件呈现为无效的 zlib 文件。
0000000 0000 5e01 9c78 904d 4f5d 5082 871c fa9f
0000020 353e 25cd 6975 2c2f d563 4c2c 62b8 cad1
我们可以看到上面的第 5-6 个字节是 9C 78
,这是 zlib 签名,但在这些之前的 4 个字节是问题。
要检查压缩数据是否正确,我执行以下操作:
dd if=file.token bs=1 skip=4 | openssl zlib -d
这会产生预期的结果(用于测试)。
问题在于应用程序将这些数据读回到数据对象中:
QFile f( _tokenFile);
if (!f.exists()) {
qDebug() << "Token file doesn't exist" << f.fileName();
return false;
}
if (!f.open( QIODevice::ReadOnly)) {
qDebug() << "Unable to open token file for reading" << f.errorString();
return false;
}
QByteArray tokenBa = f.readAll();
f.close();
if (tokenBa.isEmpty()) {
qDebug() << "Token file is empty.";
return false;
}
tokenBa = qUncompress( tokenBa);
QDataStream ds( &tokenBa,QIODevice::ReadOnly);
ds >> _token;
这将返回 null - 因为前导 4 个无关字节。我可以输入一些代码来跳过这 4 个前导字节,但我怎么知道它总是 4 个字节?相反,我想确定文件数据都是 zlib 压缩的。
我的问题是如何避免首先保存这些字节,以便在重新读取时知道格式是 zlib 类型?
解决方法
您无法避免它们,因为稍后 qUncompress 需要它们:
注意:如果要使用此函数解压缩使用 zlib 压缩的外部数据,首先需要在包含数据的字节数组前添加一个四字节的标头。标头必须包含未压缩数据的预期长度(以字节为单位),表示为无符号、大端、32 位整数。