原因:
由于服务器存在大数据量日志.日志使用tgz格式压缩的。数据量降低到了原来的1/20差不多。如果处理需要进行解压,查询就太麻烦了。而且需要的空间一下子增加了好多。然后就想要做一个小程序,支持对压缩文件直接进行处理。
首先就想到了对现有的开源压缩解压库。没找到线程的,就用7z把。反正也比较容易转成库。
我用的是vs2008 sp1。 在通过取消7zCrcOpt.asm和AesOpt.asm的ml -omf参数后,编译就OK了。
使用vs2008的时候会提示 xmm1, xmm2, xmm3找不到的问题
编译CPP/7Zip/Bundles/Format7ZF生成 DLL
编译CPP/7Zip/UI/COnsole生成 exe
具体各个文件夹的作用可以参考readme.txt
功能测试:
把编译的7z.dll 放在7z.exe相同路径下。进行测试。
开始代码阅读前,可以大体上了解一下工具的功能。由于我主要是想要使用Lib的压缩解压功能,所以使用以下命令:
7z.exe [command] [switch] [archiveFileName] [fileName fileName]
7z.exe a -ttar 1.tar 1.txt 2.txt
7z.exe a -tgzip 1.tar.gz 1.tar
7z.exe e -tgzip 1.tar.gz
7z.exe e -ttar 1.tar
代码历程:
首先是解析参数
CArchiveCommandLineOptions options; // 可以看看具体的结构体。就知道所有的相关压缩解压设定都会在该结构内体现
parser.Parse1(commandStrings, options); // 解析了输入参数。主要是解析switch参数
parser.Parse2(options); // 生成options了。参数解析完毕。后续要进行具体操作了
CCodecs *codecs = new CCodecs; // 加载编码器
CMyComPtr<
#ifdef EXTERNAL_CODECS
ICompressCodecsInfo
#else
IUnknown
#endif
> compressCodecsInfo = codecs;
HRESULT result = codecs->Load();
CIntVector formatIndices;
if (!codecs->FindFormatForArchiveType(options.ArcType, formatIndices)) // 通过-t提供的类型获取操作类型
throw kUnsupportedArcTypeMessage;
后续根据刚才的command参数觉得执行入口
if (options.Command.CommandType == NCommandType::kInfo)
{
}
else if (options.Command.CommandType == NCommandType::kBenchmark)
{
}
else if (isExtractGroupCommand || options.Command.CommandType == NCommandType::kList) // 解压缩在这里
{
}
else if (options.Command.IsFromUpdateGroup()) // 压缩在这里
{
}
else
PrintHelpAndExit(stdStream);
准备回调,还有具体解压缩参数
CExtractCallbackConsole *ecs = new CExtractCallbackConsole; // 这个是回调
CExtractOptions eo; // 这个是参数
后面就是解压缩了
HRESULT result = DecompressArchives(
codecs,
formatIndices,
options.ArchivePathsSorted,
options.ArchivePathsFullSorted,
options.WildcardCensor.Pairs.Front().Head,
eo, &openCallback, ecs, errorMessage, stat);
过程中多次调用回调返回信息。然后就结束了。主体流程完成。
后面深入体会一下解压流程
HRESULT result = archiveLink.Open2(codecs, formatIndices2, options.StdInMode, NULL, arcPath, openCallback); // 打开压缩文件。该函数把每种格式都打开了一个对应的文件流或者内存流。
if (result == E_ABORT)
return result;
// 在这个函数内部,我们看到了压缩解压实际代码的身影
(1) CArchiveLink::Open2 -> CArchiveLink::Open -> CArc::OpenStreamOrFile -> CArc::OpenStream
CMyComPtr<IInArchive> archive;
FormatIndex = orderIndices[i];
RINOK(codecs->CreateInArchive(FormatIndex, archive));
虽然封装成了Com DLL形式。导致通过调试就可以知道,其实这个archive就是指向CHandler对象。gzip指向的是CArchieve::NGz::CHandler对象。
后续是CHandler的第二步
(2) if (stream)
result = archive->Open(stream, &kMaxCheckStartPosition, callback); // 3个参数 stream 压缩文件流 kMaxCheckStartPosition(无效) 解压文件起始位置 callback(无效) 回调类
CHandler Open 结束
DecompressArchive(arc,
fi.Size + archiveLink.VolumesSize,
wildcardCensor, options, extractCallback, extractCallbackSpec, errorMessage, packProcessed) // 解压缩单个文件了
其中调用了Archieve函数用来进行解压缩
(3) 进行解压缩
result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, extractCallbackSpec);
所以,7z对于解压的封装,中间整个文件的decode。是没有中间流的概念的。并不是和在内存中对解压的大数据进行操作。
使用7z作为lib,只要2步实现该功能
1.修改DLL为Lib目标
2.修改Calling Convention方式为 __cdecl