Introduction
The ZipArchive Library can create segmented archives using two methods: splitting
and spanning. The internal structure of archives created in both ways is the same;
the difference in use is summarized below:
|
Destination media |
not limited to any |
removable |
|
Archive Structure |
splits into volumes
(usually in the same folder) |
spans multiple disks |
|
Naming |
extension is based on the volume number,
the last volume's extension is "zip"
(it is possible to implement a custom naming scheme) |
each volume has the same name |
|
Single Volume Size |
declared by the user when creating an archive |
auto-detected from the free space on the current disk |
|
Callback |
not needed, but possible |
needed for changing volume |
Conversion Between Split and Spanned Archives
To convert between split and spanned archives, it is enough to change the names
of volumes and copy the volumes to appropriate locations.
- To convert a spanned archive to a split archive, copy all the volumes into one location
and rename their extensions according to the
printf function format
using the pattern: z%.2d. For the volumes numbers greater than 99 this
pattern becomes z%d. Use the one-based volumes number as an argument.
Use the "zip" extension for the last volume. This way the volumes are
names this way:
- name.z01
- name.z02
- ...
- name.z100
- ...
- name.zip
- To convert a split archive to a spanned archive, copy each volume to a separate
removable media, giving it the "zip" extension. You also should name each disk with
the appropriate label starting from "pkback# 001"
(note the space between '#' and '0').
Limits in Number of Volumes
In the standard zip format, the number of volumes is limited to:
|
Standard Zip Format |
65,535 |
999 |
|
Zip64 Format |
4,294,967,295 - 1 |
4,294,967,295 - 1 |
For more information on the Zip64 format, see
Zip64 Format: Crossing the Limits of File Sizes and Number of Files and Segments.
Splitting: All Volumes in One Folder
The volumes of a split archive are usually located in the same folder. You need
to specify a size of a single volume when creating a split archive. Internal zip
structures such as file headers, are not split across volumes. This may result in
a volume size being slightly smaller from the declared size, when the structure
could not fit entirely into the current volume and it was stored in the next volume.
If the declared volume size is too small to hold an entire internal structure, this
particular volume will be enlarged. Generally you should not use values less than
64KB for volumes sizes.
Under Linux, when you are opening an existing split archive, set
uVolumeSize
to a value different from
0 when calling the
CZipArchive::Open(LPCTSTR) method. This is caused by the lack of the implementation
of
ZipPlatform::IsDriveRemovable() and the device containing
the archive is always assumed to be removable.
Sample Code
LPCTSTR zipFileName = _T("C:\\Temp\\test.zip");
CZipArchive zip;
zip.Open(zipFileName, CZipArchive::zipCreateSegm, 1024 * 1024);
zip.AddNewFile(_T("C:\\Temp\\big.dat"));
zip.Close();
zip.Open(zipFileName);
zip.ExtractFile(0, _T("C:\\Temp"), false, _T("big.ext"));
zip.Close();
Using Callback with Split Archives
Using callback with split archives is not necessary, but possible. This is useful
when you need to implement a custom volumes' naming scheme or to have the possibility
to prompt a user for a location of a volume, when it was not found.
When the callback is set, the
CZipCallback::Callback
method will be called every time a volume change occurs.
- The reason for calling the callback is stored in CZipSegmCallback::m_iCode
and takes one of the CZipSegmCallback::SegmCodes values.
- You can change the filename and path of the current volume by modifying the CZipCallback::m_szExternalFile variable. When the callback
is called, this variable holds the full path to the volume file as expected by the
library, but you can change it and the library will create the volume file under
a new name or location.
- The number of the disk needed for reading or writing is stored in
CZipSegmCallback::m_uVolumeNeeded.
- To abort the archive processing, return
false from this method. A CZipException will be thrown with the CZipException::aborted
code.
- The value of the
uProgress parameter is set to 0 apart
from the time when the callback is called for the last volume. It is then set to
ZIP_SPLIT_LAST_VOLUME.
- When creating a split archive, the callback is called twice for the last volume.
- The first time it is called when the library doesn't know yet that the current volume
is the last volume and the value of the
uProgress parameter is set
to 0.
- The second time it is called when the library already knows that the current volume
is the last volume and the value of the
uProgress parameter is set
to ZIP_SPLIT_LAST_VOLUME.
Sample Code
class CSplitCallback : public CZipSegmCallback
{
bool Callback(ZIP_SIZE_TYPE uProgress)
{
switch (m_iCode)
{
case scVolumeNeededForRead:
case scVolumeNeededForWrite:
case scFileNameDuplicated:
{
if (m_iCode == scFileNameDuplicated)
{
if (!ZipPlatform::RemoveFile(m_szExternalFile))
{
_tprintf(_T("Removing of the existing file failed."));
return false;
}
}
CZipPathComponent zpc(m_szExternalFile);
if (uProgress == ZIP_SPLIT_LAST_VOLUME)
zpc.SetExtension(_T("zip"));
else
{
CZipString szExt;
DWORD vol = m_uVolumeNeeded;
szExt.Format(_T("vol%.4u"), vol);
zpc.SetExtension(szExt);
}
m_szExternalFile = zpc.GetFullPath();
break;
}
case scFileCreationFailure:
_tprintf(_T("Could not create the file. \
Check, if you have write permissions to the given location.\r\n"));
return false;
case scFileNotFound:
_tprintf(_T("The given volume could not be found.\r\n"));
return false;
default:
_tprintf(_T("An unexpected code detected.\r\n"));
return false;
break;
}
return true;
}
};
void SplittingWithCallback()
{
LPCTSTR zipFileName = _T("C:\\Temp\\test.zip");
CZipArchive zip;
CSplitCallback callback;
zip.SetSegmCallback(&callback, CZipArchive::scSplit);
zip.Open(zipFileName, CZipArchive::zipCreateSegm, 1024 * 1024);
zip.AddNewFile(_T("C:\\Temp\\big.dat"));
zip.Close();
zip.Open(zipFileName);
zip.ExtractFile(0, _T("C:\\Temp"), false, _T("big.ext"));
zip.Close();
}
Spanning: Use on Removable Media
- A spanned archive is located on removable media and you need to specify a callback
object (with the CZipArchive::SetSegmCallback() method).
Setting the callback object is needed for creation as well as for extraction of
spanned archives.
- The CZipCallback::Callback method will be called every
time a disk change is needed.
Sample Code
#include <conio.h>
class CSpanCallback : public CZipSegmCallback
{
bool Callback(ZIP_SIZE_TYPE uProgress)
{
switch (m_iCode)
{
case scVolumeNeededForRead:
case scVolumeNeededForWrite:
_tprintf(_T("Insert the disk number %d\r\n"), m_uVolumeNeeded);
break;
case scFileNameDuplicated:
_tprintf(_T("The file with the given name already \
exists on the disk.\r\n"));
break;
case scCannotSetVolLabel:
_tprintf(_T("Cannot set the disk volume label. \
Check if the disk is not write-protected.\r\n"));
break;
case scFileCreationFailure:
_tprintf(_T("Could not create file. \
Check if the disk is not write-protected.\r\n"));
break;
default:
_tprintf(_T("An unexpected code detected.\r\n"));
return false;
break;
}
_getch();
_tprintf(_T("...\r\n"));
return true;
}
};
void Spanning()
{
LPCTSTR zipFileName = _T("a:\\test.zip");
CZipArchive zip;
CSpanCallback callback;
zip.SetSegmCallback(&callback);
zip.Open(zipFileName, CZipArchive::zipCreateSegm, ZIP_AUTODETECT_VOLUME_SIZE);
zip.AddNewFile(_T("C:\\Temp\\big.dat"));
zip.Close();
zip.Open(zipFileName);
zip.ExtractFile(0, _T("C:\\Temp"), false, _T("big.ext"));
zip.Close();
}
Detecting the Last Disk in a Drive
When extracting a spanned archive, you need to insert the last disk into the drive
before opening the archive. The central directory written on it and the extraction
starts from reading the central directory. There is no simple way to detect, if
the right disk is in the drive, but the ZipArchive Library throws the
CZipException with the
CZipException::cdirNotFound
code, when the archive you are trying to open does not have the central directory.
In case of a spanned archive, it may mean that the user has not inserted the last
disk into the drive. You can catch this exception and keep prompting the user for
the right disk until the archive is successfully opened.
Callbacks Called
While processing a segmented archive the following callbacks that are called are
the most important:
To read more about using callback objects, see
Progress Notifications: Using Callback Objects.
See Also API Calls