Introduction
Compression Algorithms
Files inside zip archives can be compressed using different algorithms. The ZipArchive Library supports
deflate and
bzip2 algorithms to be used during compression of zip archives. You should choose the compression
algorithms depending
on your needs. To select it, use the
CZipArchive::SetCompressionMethod() method
before compressing
a file. There is no need to use this method when decompressing. The decompression process automatically detects
the algorithms
used.
Sample Code
CZipArchive zip;
zip.Open(_T("C:\\Temp\\test.zip"), CZipArchive::zipCreate);
zip.SetCompressionMethod(CZipCompressor::methodBzip2);
zip.AddNewFile(_T("C:\\Temp\\file1.dat"));
zip.Close();
Deflate
It is the most frequently used algorithm in zip archives and supported by all standard zip utilities. The
Zlib library provides the implementation of this algorithm (see
Acknowledgements: Credits and Used Third-Party Code Licensing Information for more
information).
Bzip2
It compresses files more efficiently than the deflate algorithm, but is slower. It is supported by PKZIP since
version 4.6
and by WinZip since version 10.0. Earlier versions of these programs will not decompress archives that use the
bzip2 algorithm.
Also, at the time of writing, Windows Compressed Folders are not capable of extracting such archives.
The implementation of this algorithm is provided by the bzip2 data compressor (see
Acknowledgements: Credits and Used Third-Party Code Licensing Information
for more information).
Enabling Bzip2 Functionality
The Bzip2 algorithm is available in the Full Version of the Library only and is enabled by default.
With CMake: Control Bzip2 support using the ZIP_ENABLE_BZIP2
option:
- Set -DZIP_ENABLE_BZIP2=ON to enable (default)
- Set -DZIP_ENABLE_BZIP2=OFF to disable
Without CMake: Set _ZIP_BZIP2 to 1 to enable or
0 to disable in the _features.h file (default is 1).
Configuring Internal vs External Bzip2 Library
The ZipArchive Library includes source files for the bzip2 algorithm. You can also use a system-installed bzip2
library.
With CMake: Use the DZIP_USE_BZIP2_INTERNAL option:
- Set -DZIP_USE_BZIP2_INTERNAL=ON to use internal sources (default for all
platforms)
- Set -DZIP_USE_BZIP2_INTERNAL=OFF to use system library
- CMake will find and link the system bzip2 library using find_package(BZip2)
Without CMake: Set _ZIP_BZIP2_INTERNAL in the _features.h file
(1 for internal sources, 0 for system library). Remember to link against the
system library.
Zstandard (ZSTD)
The ZSTD algorithm is available in the Full Version of the Library only and is enabled by default.
Zstandard, or zstd as a short version, is a fast lossless compression algorithm, targeting real-time compression
scenarios at zlib-level and better compression ratios.
It's backed by a very fast entropy stage, provided by
Huff0 and FSE library. The implementation of this algorithm is provided by the zstd data compressor (see
Acknowledgements: Credits and Used Third-Party Code Licensing Information
for more information).
Enabling Zstandard Functionality
The Zstandard algorithm is available in the Full Version of the Library only and is enabled by default.
With CMake: Control ZSTD support using the ZIP_ENABLE_ZSTD
option:
- Set -DZIP_ENABLE_ZSTD=ON to enable (default)
- Set -DZIP_ENABLE_ZSTD=OFF to disable
Without CMake: Set _ZIP_ZSTD to 1 to enable or
0 to disable in the _features.h file (default is 1).
Configuring Internal vs External Zstandard Library
The ZipArchive Library includes source files for the Zstandard algorithm. You can also use a system-installed
zstd library,
though this may require careful configuration to ensure compatibility.
With CMake: Use the ZIP_USE_ZSTD_INTERNAL option:
- Set -DZIP_USE_ZSTD_INTERNAL=ON to use internal sources (default for all
platforms)
- Automatically configures ZSTD_MULTITHREAD and ZSTD_STATIC_LINKING_ONLY
- On x64 Linux/macOS builds, enables ASM optimizations for better performance
- Set -DZIP_USE_ZSTD_INTERNAL=OFF to use system library
- CMake will find and link the system zstd library using find_package(zstd)
- The system library should be compiled with ZSTD_MULTITHREAD and
ZSTD_STATIC_LINKING_ONLY symbols defined for the full features support, but that is
not mandatory
Without CMake: Set _ZIP_ZSTD_INTERNAL in the _features.h file
(1 for internal sources, 0 for system library). When using an external library,
ensure ZSTD_MULTITHREAD and
ZSTD_STATIC_LINKING_ONLY are defined the same way there as in the system library. Remember to
link against the system library.
To enable hardware accelleration, see
Compilation of the ZipArchive Library and Integration with Applications.
Easy Single File Compression
To quickly add a file to an archive, use the
CZipArchive::AddNewFile(CZipAddNewFileInfo&)
method or one of its overloads. You need to specify the file to compress. Additionally, you may specify:
Sample Code
CZipArchive zip;
zip.Open(_T("C:\\Temp\\test.zip"), CZipArchive::zipCreate);
zip.AddNewFile(_T("C:\\Temp\\file1.dat"));
zip.AddNewFile(_T("C:\\Temp\\file2.dat"), _T("renamed.dat"));
zip.AddNewFile(_T("C:\\Temp\\file3.dat"), 0);
zip.AddNewFile(_T("C:\\Temp\\file4.dat"), -1, false);
zip.Close();
Callbacks Called
The methods for easy compression can call the following callbacks to notify about the progress:
To read more about using callback objects, see
Progress Notifications: Using Callback Objects.
Easy Multiple Files Compression
To quickly add a file to an archive, use one of the
CZipArchive::AddNewFiles()
methods.
You need to specify the directory that contains the files to compress. Additionally, you may filter the files
and specify:
Using Filters
To have more control over which files are added to an archive, you can use the filters with the
CZipArchive::AddNewFiles()
method.
Sample Code
using namespace ZipArchiveLib;
class CSizeFileFilter : public CFileFilter
{
ZIP_FILE_USIZE m_uMinSize;
ZIP_FILE_USIZE m_uMaxSize;
public:
CSizeFileFilter(ZIP_FILE_USIZE uMinSize, ZIP_FILE_USIZE uMaxSize, bool bInverted = false)
:CFileFilter(bInverted), m_uMinSize(uMinSize), m_uMaxSize(uMaxSize)
{
}
bool Accept(LPCTSTR, LPCTSTR, const CFileInfo& info)
{
return info.m_uSize >= m_uMinSize && info.m_uSize <= m_uMaxSize;
}
};
void EasyMultiCompress()
{
CZipArchive zip;
zip.Open(_T("C:\\Temp\\test.zip"), CZipArchive::zipCreate);
CGroupFileFilter groupFilter;
groupFilter.Add(new CSizeFileFilter(0, 20 * 1024));
groupFilter.Add(new CNameFileFilter(_T("*.tmp"), true));
groupFilter.Add(new CNameFileFilter(_T("*.dat"), true));
groupFilter.Add(new CNameFileFilter(_T("*.zip"), true));
groupFilter.Add(new CNameFileFilter(_T("*tmp*"), true, CNameFileFilter::toDirectory));
groupFilter.Add(new CNameFileFilter(_T("*temp*"), true, CNameFileFilter::toDirectory));
zip.AddNewFiles(_T("C:\\Temp"), groupFilter);
zip.Close();
}
Filtering Directories
To match directories, use the
_T("*") pattern in the name filter (
ZipArchiveLib::CNameFileFilter).
The
_T("*.*") pattern would only match directories with the dot character in the name. Also, use
the
ZipArchiveLib::CNameFileFilter::toAll type.
To ignore empty directories with this filter, include the CZipArchive::zipsmIgnoreDirectories
in the iSmartLevel parameter of the CZipArchive::AddNewFiles()
method.
Sample Code
CZipArchive zip;
zip.Open(_T("C:\\Temp\\test.zip"), CZipArchive::zipCreate);
CNameFileFilter filter(_T("*"), false, CNameFileFilter::toAll);
zip.AddNewFiles(_T("C:\\Temp\\Input1\\"), filter);
zip.AddNewFiles(_T("C:\\Temp\\Input2\\"), filter, true, CZipCompressor::levelDefault, true,
CZipArchive::zipsmSafeSmart | CZipArchive::zipsmIgnoreDirectories);
zip.Close();
Additional Considerations
- The filters classes are located in the
ZipArchiveLib namespace. Use this namespace when
using filters.
- To use directory traversing functionality in your application, you can reuse the ZipArchiveLib::CDirEnumerator
class.
Callbacks Called
When adding multiple files, the following callbacks are called:
To read more about using callback objects when performing multiple operations, see
Progress Notifications: Using Callback Objects.
Advanced Compression: More Control Over How Data is Written
The
CZipArchive::AddNewFile(CZipAddNewFileInfo&) method and its overrides do
most of the
work for you, however you may want to have more control over this process. To manually compress a file follow
these steps:
- Prepare a CZipFileHeader template and fill it with required data. To read
what data is reused
from the template when adding a file, see the CZipArchive::OpenNewFile()
method documentation.
- Open a new file record in the archive with the CZipArchive::OpenNewFile()
method.
- Compress the data by repeatedly calling the CZipArchive::WriteNewFile()
method.
- When you have no more data to compress, call the CZipArchive::CloseNewFile() method. If an
exception was thrown while compressing data, call this method with the
bAfterException
parameter set to true
to prevent further exceptions being thrown when closing.
Sample Code
CZipArchive zip;
zip.Open(_T("C:\\Temp\\test.zip"));
CZipFileHeader templ;
templ.SetFileName(_T("data.txt"));
templ.SetSystemAttr(FILE_ATTRIBUTE_READONLY);
zip.OpenNewFile(templ, 9);
LPCTSTR data1 = _T("This is data\r\n");
LPCTSTR data2 = _T("to be written");
zip.WriteNewFile(data1, (DWORD)(_tcslen(data1) * sizeof(TCHAR)));
zip.WriteNewFile(data2, (DWORD)(_tcslen(data2) * sizeof(TCHAR)));
zip.CloseNewFile();
zip.Close();
Adding Directories
You can add a directory in two ways:
- To add an existing directory, you can use any of the
CZipArchive::AddNewFile() methods.
- To add a non-existing directory, use the CZipArchive::OpenNewFile() method.
The ZipArchive Library treats files ending with a path separator like directories.
Sample Code
CZipArchive zip;
zip.Open(_T("C:\\Temp\\test.zip"), CZipArchive::zipCreate);
zip.AddNewFile(_T("c:\\windows"), CZipCompressor::levelStore);
CZipFileHeader header;
header.SetSystemAttr(ZipPlatform::GetDefaultDirAttributes());
header.SetFileName(_T("empty dir"));
header.SetModificationTime(time(NULL));
zip.OpenNewFile(header, CZipCompressor::levelStore);
zip.CloseNewFile();
zip.Close();
Other Functionality
Adding Files From Other Archives
If you wish to add to your archive files from other archives and you would like to avoid extracting and then
compressing
them again, use one of the following methods:
- These methods copy data from the source archive to the destination archive without decompressing it.
- If an encryption method and a password are set, the data will be encrypted while the getting process.
Data will not be encrypted,
if it already is encrypted. See Encryption Methods: How to Best Protect Your Data for information about
setting an encryption
method and a password.
Sample Code
CZipArchive zipDest;
zipDest.Open(_T("C:\\Temp\\testDest.zip"), CZipArchive::zipCreate);
CZipArchive zipSource;
zipSource.Open(_T("C:\\Temp\\test.zip"));
CZipIndexesArray indexes;
indexes.Add(0);
indexes.Add(1);
zipDest.GetFromArchive(zipSource, indexes);
zipSource.Close();
zipDest.Close();
Multithreaded Compression
Although compression to a single archive from multiple threads is not possible, you can perform multithreaded
compression
to some extent using the following steps:
- Compress files to separate archives. Use one thread per archive. For example, if
you have six files
to compress, you can put three files to one archive and the other three to another archive. In this
case, you would use
two threads.
- When all files are compressed, create a new (destination) archive and use one of the CZipArchive::GetFromArchive()
methods to copy compressed data from existing archives to the new archive. Perform this step in a single
thread.
In the example above, you would copy the compressed data to a new archive and delete the two
existing archives.
Finalizing Archives and Preventing Archive Corruption
During an archive modification, the central directory is removed from the archive and kept in memory. It is
written back
when you call
CZipArchive::Close(). However, if a crash occurs before the central
directory
is written, the archive will be unusable. You can request writing the central directory back to the archive
after each modification
with the
CZipArchive::SetAutoFinalize() method or perform it manually with the
CZipArchive::Finalize() method. You should use the finalizing methods sparingly otherwise the
performance can
be degraded.
The
CZipArchive::Finalize() (called manually or automatically) will not execute
when there
are any pending changes. See
Modification of Archives: Replacing, Renaming, Deleting and Changing Data for more information.
To flush file buffers alone without writing the central directory to the disk, call the
CZipArchive::FlushBuffers()
method.
When removing files, you can remove them only from the central directory for safety. See
CZipArchive::RemoveFile
for more information (set the
bRemoveData parameter to
false).
Segmented Archives
If you finalize a segmented archive in creation, it will not be closed, but its state will be changed from "an
archive in
creation" to "an existing segmented archive". Finalize a segmented archive, when you have finished adding files
to it and
you want to begin extracting or testing it. This means that you can finalize a segmented archive only once.
However, if
after finalizing a segmented archive it turns out that the archive is one segment only, the archive is converted
to a normal
archive and you can use it as such. If you want to know what is the state of the archive after finalizing it,
call the
CZipArchive::GetStorage() and then the
CZipStorage::IsSegmented()
method. The method will return
true if the archive was converted to a normal archive.
Committing Modification Changes
To prevent archive corruption you may also want to adjust the commit changes mode. See
Modification of Archives: Replacing, Renaming, Deleting and Changing Data
for more information.
- Auto-Finalize cannot be enabled, if there are any uncommitted changes pending.
System Compatibility
- Each file inside an archive has a flag set which tells for which platform the file is intended. This can
be one of ZipCompatibility::ZipPlatforms values.
- The compatibility settings affect the conversion of file attributes - file attributes are defined
differently across the
platforms.
- When opening an existing archive, the ZipArchive Library assumes the system compatibility of the whole
archive to be the
same as the compatibility of the first file in the archive (if the one is present). This will affect
only newly added files.
In other cases the current system value is assumed which is determined by the ZipPlatform::GetSystemID()
method call during creating or opening an archive.
- You can set the system compatibility with the CZipArchive::SetSystemCompatibility() method.
- When a file in an archive that was created under Windows, contains Unix attributes stored in bits 31-16,
and the current system is Linux/macOS, those Unix attributes will be used instead of trying to convert
DOS attributes to Unix attributes. Still \c 755 will be used for folders.
Setting Compressor Options
You can adjust the options of the
Deflate or the
Bzip2
compressor by calling the
CZipArchive::SetCompressionOptions() method providing as
an argument
an appropriate options object derived from the
CZipCompressor::COptions
class.
Use
ZipArchiveLib::CDeflateCompressor::COptions and
ZipArchiveLib::CBzip2Compressor::COptions, respectively. Please refer to the sample
code below
and the documentation of these classes.
Sample Code
#include "DeflateCompressor.h"
#include "Bzip2Compressor.h"
using namespace ZipArchiveLib;
void SetOptions()
{
CZipArchive zip;
CDeflateCompressor::COptions deflateOptions;
deflateOptions.m_iBufferSize = 4 * 65536;
zip.SetCompressionOptions(&deflateOptions);
CBzip2Compressor::COptions bzip2Options;
bzip2Options.m_iBufferSize = 65536;
zip.SetCompressionOptions(&bzip2Options);
}
Additional Considerations (Windows Only)
When your system utilizes large amount of memory while extensive file operations, see
Modification of Archives: Replacing, Renaming, Deleting and Changing Data
for a possible solution.
See Also API Links