我正在Windows平台上用C++编写一个程序。我想压缩一些存储在
char[]
数组中的数据,并将其输出到文件中,稍后我会将文件上传到unix服务器,我希望它可以通过gzip -d
解压缩。
经过大量研究,我选择了miniz。另外,我在here找到了 gzip 文件格式。
这是创建gzip文件的代码片段:(抱歉我没有放置一些变量的定义;它们是在其他地方定义的)
unsigned long zsize;
zpkg[0] = 0x1F;
zpkg[1] = 0x8B;
zpkg[2] = 8;
zpkg[3] = 0;
zpkg[4] = 0;
zpkg[5] = 0;
zpkg[6] = 0;
zpkg[7] = 0;
zpkg[8] = 0;
zpkg[9] = 0xFF;
compress2(zpkg + 10, &zsize, pkg, pkgSize, MZ_DEFAULT_LEVEL);
int footerStart = (int)zsize + 10;
mz_ulong crc = mz_crc32(MZ_CRC32_INIT, zpkg + 10, zsize);
zpkg[footerStart] = crc & 0xFF;
zpkg[footerStart + 1] = (crc >> 8) & 0xFF;
zpkg[footerStart + 2] = (crc >> 16) & 0xFF;
zpkg[footerStart + 3] = (crc >> 24) & 0xFF;
zpkg[footerStart + 4] = pkgSize & 0xFF;
zpkg[footerStart + 5] = (pkgSize >> 8) & 0xFF;
zpkg[footerStart + 6] = (pkgSize >> 16) & 0xFF;
zpkg[footerStart + 7] = (pkgSize >> 24) & 0xFF;
然后将
zpkg
数组输出到文件中。但这是行不通的;当我用gzip解压它时,错误消息是:
gzip: data stream error
gzip: test.gz: uncompress failed
有人可以指出我做错了什么吗?
感谢马克·阿德勒和迈克尔,我找到了一个可行的解决方案。
首先,正如 Mark 指出的,我应该让 miniz 返回原始的 deflate 数据流。这可以通过将
-MZ_DEFAULT_WINDOW_BITS
(注意减号)传递给 mz_deflateInit2()
作为第四个参数来完成。查看miniz源码,compress2()
函数最终用mz_deflateInit2()
调用MZ_DEFAULT_WINDOW_BITS
,这意味着添加zlib页眉和页脚。所以最简单的解决方法是在那里添加一个减号,这样我仍然可以使用 compress2()
函数。 (这对我有用,因为我只在一个地方调用这个函数)
其次,正如 Michael 指出的,CRC 码应该在未压缩的数据上计算。所以我这样修复它:
mz_ulong crc = mz_crc32(MZ_CRC32_INIT, pkg, pkgSize);
进行上述两项更改后,
gzip -d
不再抱怨了。
compress2()
生成一个 zlib 流,它是带有 zlib 标头和尾部的 deflate 压缩数据。对于您正在做的事情,您只想将原始 deflate 压缩流粘贴到手动生成的 gzip 标头和预告片中。
您可以:a) 丢弃
compress2()
输出的前两个和后四个字节以剥离 zlib 标头和尾部,b) 使用 deflateInit2()
、deflate()
和 deflateEnd()
代替 compress2()
并选择原始 deflate 格式,或者 c) 使用相同的函数并选择 gzip 格式,并摆脱手动构建的 gzip 标头和尾部,因为 deflate()
将为您做到这一点。
我推荐c)。
使用上面的问题和答案,我能够编写工作代码。
#include "miniz_cpp.hpp" //https://github.com/nyq/miniz-cpp/tree/master
//based on miniz 3.0.2
using namespace miniz_cpp;
void Save_gz_file(std::string sFileName, std::string &data)
{
std::string sDeflated = "";
unsigned long iDeflated = data.length()*2+18;
sDeflated.reserve(iDeflated);
sDeflated.append("\x1f\x8b\x08\0\0\0\0\0\0\xff", 10);
mz_stream stream;
memset(&stream, 0, sizeof(stream));
stream.next_in = (unsigned char *) data.data();
stream.avail_in = (mz_uint32) data.length();
stream.next_out = (unsigned char *) (sDeflated.data() + 10);
stream.avail_out = (mz_uint32) iDeflated;
int result = mz_deflateInit2(&stream, MZ_BEST_COMPRESSION,
MZ_DEFLATED, -MZ_DEFAULT_WINDOW_BITS, 9,
MZ_DEFAULT_STRATEGY);
if(result == MZ_OK)
{
result = mz_deflate(&stream, MZ_FINISH);
if(result == MZ_STREAM_END)
{
std::ofstream some_file(sFileName);
some_file.write(sDeflated.data(), stream.total_out + 10);
uLong crc = mz_crc32(MZ_CRC32_INIT, (mz_uint8*) (data.data()),
data.length());
some_file.write((const char*) &crc, 4);
uLong len = data.length();
some_file.write((const char*) &len, 4);
}
mz_deflateEnd(&stream);
}
}