又开一个新坑了,压缩解压。。。
编译
windos系统
先讲讲windows下怎么编译7-zip模块吧(我已经放弃了直接使用7zip的代码)
有兴趣可以继续研究一下,最后调用一大堆接口是调用7zHandlerOut.cpp里面的CHandler::UpdateItems
最后为了可以跨平台减少工作量,我决定使用7z的动态库,不使用动态库的在CPP/7zip/UI/Console/Main.cpp里面的Main2函数(Alone7z)
首先下载7-zip的源码(如果你只需要解压常见的7z文档只[需要7zr.dll或7zxr.dll],可以考虑只下载LZMA SDK)。
http://www.7-zip.org/download.html
选择7-Zip Source code 或 LZMA SDK: (C, C++, C#, Java)
文件 | 源码所在目录 | 说明 |
7za.dll | CPP/7zip/Bundles/Format7z | 支持7z格式文件的压缩与解压 |
7zxa.dll | CPP/7zip/Bundles/Format7zExtract | 支持7z格式文件的解压 |
7zra.dll | CPP/7zip/Bundles/Format7zR* | 支持7z格式文件的压缩与解压 (仅限算法7z/LZMA/BCJ/BCJ2) |
7zxr.dll | CPP/7zip/Bundles/Format7zExtractR* | 支持7z格式文件的解压 (仅限算法7z/LZMA/BCJ/BCJ2) |
7z.dll | CPP/7zip/Bundles/Format7zF | 支持所有格式文件的压缩和解压 (7-zip这个软件支持多少就支持多少) |
以上表格的内容在7z-src的readme.txt和lzma sdk里的lzma-sdk.txt 有详细描述
*标注的文件在LZMA SDK中包含,并且实测可以解压LZMA2的7z文件
我只需要用7zra.dll,我下载的是LZMA SDK 1801
然后打开VS2015 x64 本机工具命令提示符
进入要编译的dll的源码目录,输入
nmake NEW_COMPILER=1 MY_STATIC_LINK=1 CPU=AMD64
如果不需要静态编译(就是你想带上msvc的运行库)可以去掉MY_STATIC_LINK
如果编译的是32位的dll 打开VS2015 x64 x86兼容工具命令提示符,然后去掉AMD64
然后在O或AMD64目录内就能看到你要的dll了
unix类系统
unix类系统可以下载后面的p7zip,不过似乎还没更新18.01版本
然后make Client7z
mac OS编译示例:
安装xcode
重命名makefile.macosx_llvm_64bits为makefile.machine(覆盖原文件)
执行make Client7z
在bin目录下可以找到7z.so,虽然这是.so后缀名的文件,实际上是一个bundle类型的macho对象。为了统一标识,我们可以将7z.so重命名为7z.bundle(但是在p7zip编译的7zClient内仍然使用7z.so这一文件名)
如果需要生成动态库,可以在最后链接时将参数-bundle 改成-shared 名字从bin/7z.so改成bin/7z.dylib
测试编译的文件能否正常使用
cd ~/Downloads/p7zip_16.02/bin/ ./Client7z a 1.7z 7z.so
如果看到有1.7z生成,说明生成的7z.so可以使用
7zra.dll/7z.so/7z.dylib创建7z压缩包示例
主要使用到的函数以及我不太靠谱的翻译
(以下的代码我经过修改以便理解,但可能不能跨平台编译)
extern "C" CreateObject(const GUID *clsID, const GUID *iid, void **outObject);
创建 一个特定类型的 COM对象(我们需要的是7z的IOutArchive),其中有一些常量我们需要知道
const GUID CLSID_CFormat7z = { 0x23170F69, 0x40C1, 0x278A,{ 0x10, 0x00, 0x00, 0x01, 0x10, 0x07, 0x00, 0x00 } };//7z格式
const GUID IID_IOutArchive = { 0x23170F69 ,0x40C1 ,0x278A,{ 0x00, 0x00, 0x00, 0x06, 0x00, 0xA0, 0x00, 0x00} };//IOutArchive
然后还有7z格式的IOutArchive类,这个类可以创建7z格式压缩包
class IOutArchive
{
public :
//...
virtual __declspec(nothrow) long __stdcall UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *updateCallback) = 0;
//STDMETHOD(UpdateItems)(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *updateCallback);
//...
//release()
};
这个类中我们会用到方法有两个 UpdateItems 和 release,显然release是当我们使用完毕这个类要调用的一个释放函数。
UpdateItems的几个参数我稍微做一下说明(详细说明在iArchive.h内)
outStream: 压缩包文件的输出流,这个输出流必须要具有位置查找功能(创建7z格式的压缩文件),就是要有Write,Seek和SetSize这几个方法。
numItems: 要更新的文件个数
updateCallback: 回调函数,用来传递更新进度与获取每个要被更新文件的详细信息
然后是IArchiveUpdateCallback类
class UpdateCallbackExample_7z :
public IArchiveUpdateCallback,
public ICryptoGetTextPassword2,
public CMyUnknownImp
{
public:
MY_UNKNOWN_IMP2(IArchiveUpdateCallback, ICryptoGetTextPassword2)
//让7z库知道这个类实现了IArchiveUpdateCallback与ICryptoGetTextPassword2的接口
//INTERFACE_IProgress(x);
STDMETHOD(SetTotal)(UInt64 total);
//总进度
STDMETHOD(SetCompleted)(const UInt64 *completeValue);
//已完成进度
//INTERFACE_IArchiveUpdateCallback(PURE);
STDMETHOD(GetUpdateItemInfo)(UInt32 index, Int32 *newData, Int32 *newProps, UInt32 *indexInArchive);
//获取这个Item要更新的数据(newData是更新数据,newProps是更新属性)
STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value);
//获取文件的属性
STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream);
//获取文件的输入流
STDMETHOD(SetOperationResult)(Int32 operationResult);
//当每一个被更新的item处理完会调用
//INTERFACE_IArchiveUpdateCallback2 !!! 7z.dll 不支持分卷功能,如果需要分卷,要自己在传入UpdateItems的outStream中处理
//STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size);//每一个分卷大小
//STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream);//第index个文件的输出流
//ICryptoGetTextPassword2
STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password);//加密设置(在默认情况下编译的7zra.dll中是不支持加密功能的)
//...
};
未完待续
致谢
本文使用到的7zip源码是遵循LGPL许可发布的,可以在http://www.7-zip.org下载
还有许多搜索到资料,由于太多太零散了,没在此列出