65.9K
CodeProject 正在变化。 阅读更多。
Home

LiteZip 和 LiteUnzip

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.82/5 (50投票s)

2006年3月9日

LGPL3

12分钟阅读

viewsIcon

570371

downloadIcon

6044

易于使用、占用空间小的 DLL,可让您的应用程序创建 zip 压缩包并提取其内容。适用于 C、C++、VB 及其他语言。支持 Win32 和 Linux。

LiteZip/LiteUnzip

LiteZip.dllLiteUnzip.dll 是两个 Win32 动态链接库。前者提供创建 ZIP 压缩包(即将多个文件压缩成一个 ZIP 文件)的功能。后者则提供提取 ZIP 压缩包内容的功能。

本项目主要基于 Lucian Wischik 的工作,而他的工作又基于 Jean-Loup Gailly 和 Mark Adler 的 gzip 1.1.4、zlib 和 info-zip。Lucian 的代码已被重构为纯 C 语言编写,仅使用 Win32 API,并打包成两个 DLL。(此外,还改进了错误检查,增加了一些功能,并实现了代码精简和流程优化。)

这两个 DLL 的主要优点如下:

  • 您可以从磁盘文件、内存缓冲区或管道中的 zip 压缩包解压(提取内容)。并且,您可以将这些内容解压到磁盘文件、内存缓冲区、管道,甚至是上述方式的任意组合。

    创建 zip 压缩包也是如此。您可以在磁盘、内存或管道中创建 zip 压缩包。并且,此 zip 压缩包的内容可以来自磁盘文件、内存缓冲区、管道,甚至是上述任何或所有方式的组合。

    鉴于这种灵活性,您无需在使用文件前将其写入临时目录。一个值得注意的特性是,您可以直接从嵌入式资源解压到内存缓冲区或磁盘文件,这对于安装程序非常有用。另一个有用的功能是,您可以在由系统页面文件支持的动态可增长内存中创建 zip,(即,在将某些内容压缩到内存缓冲区之前,您无需猜测内存缓冲区的分配大小。您可以让 DLL 根据需要动态地为您增长内存缓冲区)。

  • 压缩和解压功能分别位于各自的 DLL 中。因此,举例来说,如果您只需要从 ZIP 压缩包中提取文件,而不需要创建 ZIP 压缩包,那么您只需使用 LiteUnzip.dll,而不必浪费资源加载您不需要的代码(在 LiteZip.dll 中)。
  • 这些 DLL 可以被任何语言编写的程序使用(只要该语言能加载和调用标准 DLL)。这包括 C 或 C++ 编译的代码、Visual Basic 等。虽然 DLL 的源代码是用纯 C 编写的,但我们提供了已编译的 DLL,供那些只想在任何语言中使用 DLL 的人使用。同时提供了一个文本文件(Vb.txt),其中包含从 Visual Basic 使用这些 DLL 的函数定义。

    而且,DLL 的更新意味着所有使用它的程序都会自动获得更新,无需重新编译。

  • DLL 本身体积极小。例如,LiteZip.dll 仅约 40K。一个 DLL 的代码副本可以被多个正在运行的程序共享。DLL 中的只读数据也是共享的(例如,一些大型的“查找表”)。在内存有限的系统上,这可能是一个宝贵的特性。
  • DLL 支持基于密码的 zip 加密。
  • 同一套 DLL 同时支持 Unicode 和 ANSI 字符串。您不需要为 Unicode 和 ANSI 分别准备不同的 DLL,也不需要重新编译 DLL。
  • 这些 DLL 应该能在所有版本的 Windows 下工作,包括 CE。
  • 所有人类可读的字符串都存储在每个 DLL 的资源中,便于轻松创建这些 DLL 的非英语语言版本。
  • DLL 还支持读写 GZIP 格式(仅限单个文件——不是 tar 包)。

这些 DLL 的局限性在于:

  • 它们不支持磁盘分卷。

在 C/C++ 程序中使用代码

要让您的 C/C++ 代码能够创建 zip 压缩包,请将文件 litezip.lib 添加到您的项目中,并在源代码中 #include "LiteZip.h"

要让您的 C/C++ 代码能够解压压缩包,请将文件 liteunzip.lib 添加到项目中,并在源代码中 #include "LiteUnzip.h"

压缩和解压功能可以在单个应用程序中愉快共存。或者,如果您想减小体积,可以只使用您需要的那一个。

当然,您必须将 LiteZip.dll 和/或 LiteUnzip.dll 与您的应用程序一起分发。

以下代码片段展示了如何使用压缩/解压功能。它们使用 ANSI,但 #define UNICODE 将会改用 Unicode 版本的函数。为简洁起见,省略了错误检查。

示例 1 - 从现有文件创建 zip 文件(在磁盘上)

要将磁盘上的一些现有文件创建成一个磁盘上的 zip 压缩包,请执行以下操作:

  1. 调用 ZipCreateFile。您传递一个句柄的地址,希望 ZipCreateFile 在此为您提供一个 HZIP。您无需了解此句柄的细节。您只需将其传递给 LiteZip.dll 中的其他函数,就像您将 CreateFile() 的句柄传递给 ReadFile() 一样。您还需要传递您希望在磁盘上创建的 zip 压缩包的文件名。该名称可以包含完整路径,例如 "C:\My Directory\MyArchive.zip"。如果您希望使用加密,还需传递密码字符串。如果不使用加密,则传递 0

    如果成功,ZipCreateFile 将在磁盘上创建一个(空的)zip 压缩包,并填充您的 HZIP 句柄。

  2. 为您希望添加到 zip 压缩包中的每个文件调用 ZipAddFile。您需要传递由 ZipCreateFile 提供给您的 HZIP 句柄、要添加到压缩包中的文件的文件名,以及您希望它在 zip 压缩包内部的名称。(如果您希望在 zip 中使用相对目录层次结构,您可能需要修剪掉后一个名称中的一些目录。或者您可能希望在前面添加目录名称以在 zip 压缩包内创建目录层次结构。)
  3. 在您完成向 zip 压缩包添加项目后,调用 ZipClose 来完成操作。您需要传递由 ZipCreateFile 提供给您的 HZIP 句柄。

以下是上述操作的一个示例。假设我们在磁盘上有两个文件,名为 "simple.bmp" 和 "simple.txt"。我们希望将它们压缩到一个名为 "simple1.zip" 的 zip 压缩包中。

#include <Windows.h>
#include "LiteZip.h"

HZIP hz;

ZipCreateFile(&hz, "simple1.zip", 0);
ZipAddFile(hz, "simple.bmp");
ZipAddFile(hz, "simple.txt");
ZipClose(hz);

下载的示例 zip 文件包含一个类似的示例,带有错误检查,并且还动态链接到 LiteZip.dll。(使用动态链接时,您不需要将 LiteZip.lib 添加到您的项目中。并且 LiteZip.dll 在您的应用程序首次启动时不会被加载。它只在您调用 LoadLibrary 时才被加载)。

示例 2 - 将 zip 文件(在磁盘上)解压到磁盘上的文件

要将磁盘上的 zip 压缩包解压其内容到磁盘上,请执行以下操作:

  1. 调用 UnzipOpenFile。您传递一个句柄的地址,希望 UnzipOpenFile 在此为您提供一个 HUNZIP。您无需了解此句柄的细节。您只需将其传递给 LiteUnzip.dll 中的其他函数。您还需要传递您希望解压的 zip 压缩包的文件名。该名称可以包含完整路径。如果 zip 已加密,您还需要传递所需的密码字符串。如果不使用加密,则传递 0

    如果成功,UnzipOpenFile 将打开磁盘上的 zip 压缩包,并填充您的 HUNZIP 句柄。

  2. 调用 UnzipGetItem 来确定 zip 压缩包中有多少个项目(文件)。您向 UnzipGetItem 提供一个 ZIPENTRY 结构体。(这在 LiteUnzip.h 中定义)。您可以使用内存函数如 malloc 来分配这个结构体,或者在栈上声明它,或者将其声明为全局变量等。(如果您分配了它,您需要负责释放它)。在将它传递给 UnzipGetItem 之前,将 ZIPENTRY 的 Index 字段设置为 -1。您还需要传递由 UnzipOpenFile 提供给您的 HUNZIP 句柄。

    UnzipGetItem 会将 ZIPENTRYIndex 字段设置为 zip 压缩包内的项目数量。

  3. 循环调用 UnzipGetItemUnzipItemToFile 来从压缩包中提取每个项目,并将其保存在磁盘上。

    要提取一个项目,您首先将 ZIPENTRYIndex 字段设置为您希望提取的项目的索引(其中 0 是第一个项目,1 是第二个项目,2 是第三个项目,以此类推)。

    将您的 ZIPENTRY 和由 UnzipOpenFile 提供的 HUNZIP 句柄传递给 UnzipGetItemUnzipGetItem 将用该项目的信息填充 ZIPENTRY。这包括它的名称、未压缩的大小、修改日期等。如果您只想提取一个特定的项目,而不是调用 UnzipGetItem,您可以将所需项目的名称填入 ZIPENTRYName 字段,然后将您的 ZIPENTRY 传递给 UnzipFindItem,以用该项目的其他信息填充您的 ZIPENTRY

    最后,调用 UnzipItemToFile 将该项目提取到磁盘文件中。传递您的 ZIPENTRY、由 UnzipOpenFile 提供的 HUNZIP 句柄,以及您希望项目保存到的文件名。(如果您想使用与压缩包内相同的名称,可以使用 ZIPENTRYName 字段)。UnzipItemToFile 将提取项目并将其保存到磁盘,并创建任何需要的目录。

  4. 在您完成从 zip 压缩包中提取项目后,调用 UnzipClose 来完成操作。您需要传递由 ZipOpenFile 提供给您的 HUNZIP 句柄。

以下是上述操作的一个示例。假设我们在磁盘上有一个名为 "simple1.zip" 的 zip 压缩包。我们将提取其所有项目,使用与压缩包内相同的文件名。不使用加密。

#include <Windows.h>
#include "LiteUnzip.h"

HUNZIP   huz;
ZIPENTRY ze;
DWORD    numitems;

ZipOpenFile(&huz, "simple1.zip", 0);

ze.Index = (DWORD)-1;
UnzipGetItem(huz, &ze);
numitems = ze.Index;

for (ze.Index = >0; ze.Index < numitems; ze.Index++)
{
   UnzipGetItem(huz, &ze);
   UnzipItemToFile(huz, ze.Name, &ze);
}

UnzipClose(huz);

下载的示例 UnzipFile 包含一个类似的示例,带有错误检查,并且还动态链接到 LiteUnzip.dll。(使用动态链接时,您不需要将 LiteUnzip.lib 添加到您的项目中。并且 LiteUnzip.dll 在您的应用程序首次启动时不会被加载。它只在您调用 LoadLibrary 时才被加载)。

以下是从同一个 zip 压缩包中仅提取名为 "readme.txt" 的项目的示例。

HUNZIP   huz;
ZIPENTRY ze;

ZipOpenFile(&huz, "simple1.zip", 0);
lstrcpy(ze.name, "readme.txt");
UnzipFindItem(huz, &ze, 0); // Pass a 1 for case-insensitive find
UnzipItemToFile(huz, ze.Name, &ze);
UnzipClose(huz);

示例 3 - 从资源直接解压到内存

这种技术对于小型游戏很有用,您希望将所有数据文件捆绑在可执行文件内部,但通过先压缩它们来减小其大小。它也可能对安装程序有用,其中要安装的文件被压缩到一个嵌入在安装程序 EXE 资源中的压缩包里。

假设我们的项目有一个 .RC 文件,其中包含以下行:

1 RCDATA "file.zip"

来将 zip 文件作为资源嵌入。我们还假设这个 zip 压缩包包含一个名为 "sample.jpg" 的项目,我们希望将这一个项目解压到内存缓冲区中。

该技术与上面的解压示例非常相似,除了:

  1. 我们调用 UnzipOpenBuffer 而不是 UnzipOpenFile。毕竟,zip 压缩包不是磁盘上一个单独的 zip 文件,而是我们 EXE 资源的一部分。第三个参数是资源 ID 号。我们在上面的 RCDATA 语句中使用了数字 1
  2. 我们调用 UnzipItemToBuffer 而不是 UnzipItemToFile。毕竟,我们想将项目解压到我们创建的内存缓冲区中,而不是磁盘上的文件。注意,UnzipFindItem 会用我们需要的缓冲区大小填充 ZIPENTRYUncompressedSize 字段。所以我们用 GlobalAlloc 来分配它(或者在 C++ 中,您可以使用 new)。
  HUNZIP   huz;
  ZIPENTRY ze;
  char     *buffer;

  UnzipOpenBuffer(&huz, 0, 1, 0)

  lstrcpy(ze.name, "sample.jpg");
  UnzipFindItem(huz, &ze, 0);

  buffer = (char *)GlobalAlloc(GMEM_FIXED, ze.UncompressedSize);

  UnzipItemToBuffer(huz, buffer, ze.UncompressedSize, &ze)

  UnzipClose(huz);
  
  // Here you would do something with the contents of buffer.

  GlobalFree(buffer);

下载的示例 UnzipResource 展示了一个安装程序 EXE 如何解压嵌入在其资源中的压缩包的全部内容。

其他示例

您也可以将一些现有文件压缩到一个在内存缓冲区中创建的压缩包中。您可以提供自己的内存缓冲区(并确保它足够大以容纳生成的压缩包),或者您可以简单地让 LiteZip.dll 从系统分页内存中分配缓冲区。在后一种情况下,DLL 可以根据需要动态地增长缓冲区。

此外,您还可以添加某个内存缓冲区的内容。

下载的示例 ZipMemory 展示了将内存缓冲区的内容压缩到内存中创建的压缩包。该示例让 DLL 为生成的压缩包分配系统分页内存。它与上面的压缩示例类似,除了:

  1. 我们调用 zipCreateBuffer 而不是 ZipCreateFile。毕竟,zip 压缩包不会在磁盘上创建,而是在内存中创建。第三个参数是可增长大小的最大限制。您可以将这个数字设置得非常大,因为内存只会被保留,而不会在需要之前实际提交。
  2. 我们调用 ZipAddBuffer 而不是 ZipAddFile。毕竟,我们想要压缩的是内存缓冲区的内容,而不是磁盘上某个现有的文件。
  3. 因为我们让 DLL 分配可增长的内存,而不是提供我们自己的缓冲区,所以我们必须调用 ZipGetMemory 来检索 DLL 创建 zip 压缩包时所在的缓冲区。我们不需要调用 ZipClose,因为 ZipGetMemory 会为我们做这件事。我们也有责任释放那块内存。

有时,您可能需要压缩一些数据,而生成的压缩包没有 ZIP 头,也没有 ZIP "中央目录"。我将此称为"原始" (raw) zip。例如,压缩的 ID3 标签就是这种情况。为此,LiteZip 提供了一些函数来向原始压缩包添加数据:ZipAddFileRawZipAddHandleRawZipAddPipeRawZipAddBufferRaw。这样的压缩包只能添加一个项目。要稍后从此压缩包中解压数据项,您需要使用 LiteUnzip 的一个函数来打开原始压缩包:UnzipOpenFileRawUnzipOpenBufferRawUnzipOpenHandleRaw。然后,您可以通过调用 UnzipGetItem 来解压这一个项目,但首先您需要知道压缩包的压缩后大小以及项目未压缩时的大小。在调用 UnzipGetItem 之前,您需要将这两个值分别填入 ZIPENTRYCompressedSizeUncompressedSize 字段。示例 ZipMemoryRaw 展示了如何创建原始压缩包。而示例 UnzipMemRaw 展示了如何从同一个原始压缩包中提取这一个项目。

更新

  • 2006 年 3 月 11 日:向 LiteZip.dll 添加了函数 ZipAddDir(),以便轻松地压缩一个目录的内容(包括其子目录的内容)。还包含了一个新的 C 语言示例 ZipDir 来演示此功能。
    注意ZipAddDir 不会将空的子目录添加到 zip 压缩包中。
  • 2008 年 8 月 8 日:添加了对“原始”压缩包的支持。
© . All rights reserved.