Lỗi split or spanned archives are not supported khi deploy năm 2024

In general, the HIRO will not create a single zip file larger than 1.5 GB in size because files of that size can often cause problems when you attempt to open them (particularly with older or 32-bit operating systems). However, it is not uncommon for users to request image data in amounts that would exceed this size even with the best compression software available. As a result, the HIRO takes advantage of a zip feature known as "volumes" (also known as a split or spanned archive).

When zipping large amounts of data, the HIRO will use its archiving software to split the large zip file into several volumes. Each volume has the same maximum size; once that size is reached for the first volume file a new volume file is created. This process continues until all of the data has been compressed. For example, assume you have requested a large number of scans and when compressed your data shrinks to 4.75 GB in size. If the HIRO were to provide you with a single 4.75 GB zip file, you might encounter problems when you attempted to open or unzip it (in fact, zip files of this size are impossible to open on 32-bit systems). Splitting the zip file into 1 GB volumes would circumvent this issue, and would look like this:

FILE LISTINGFile NameSizeMyImageData.zip.0011 GBMyImageData.zip.0021 GBMyImageData.zip.0031 GBMyImageData.zip.0041 GBMyImageData.zip.005750 MB

Although zipping in this manner splits the zip file into smaller files, it is still technically a single zip archive (that is, it is not five individual zip files). To successfully unzip the archive, you will need all the files, and you should only attempt to unzip the first volume (the file ending in .zip.001). Your zip program will automatically recombine the volumes and unzip everything at once. Zipping data in this manner is still considered lossless compression, so all of your data will be intact in its original form.

Unzipping a Split Archive Under Microsoft Windows

Unfortunately, the zip utility that is built into Windows cannot unzip split archives. To unzip split archives under Windows, the HIRO recommends the 7-Zip Utility. This free utility is relatively simple to use and can compress and uncompress files in a wide variety of formats. To unzip the example above, you can right-click on the MyImageData.zip.001 file (after you've installed 7-Zip), select the 7-Zip menu, and then choose one of the "extract" options.

Unzipping a Split Archive Under Mac OS X

Unfortunately, the zip utility that is built into OS X cannot unzip split archives. To unzip split archives under OS X, the HIRO recommends the Keka File Archiver Utility. This free utility is relatively simple to use and can uncompress files in several formats. To unzip the example above, double-click on the MyImageData.zip.001 file after you've installed Keka. The Linux p7zip command line program is also available for OS X. The HIRO is only able to provide limited support for Macs.

Unzipping a Split Archive Under Linux

To unzip split archives under Linux, the HIRO recommends the p7zip utility. This command line utility is included by default in many flavors of Linux, and is available as an RPM package. To unzip the example above, you would use the following command:

user[at]localhost ~ $ 7za x MyImageData.zip.001

The 'x' option will extract the archive while maintaining full paths. To learn more about the 7za command, the HIRO recommends you review the 7za man page as well as the shared documentation installed by the package. It's quite extensive.

The file format used by WinZip® to create split Zip files is an extension to the Zip 2.0 standard. As a result, some Zip utility programs may not be able to open split Zip files (.zip or .zipx).

However, the only differences between split Zip files and spanned Zip files, which are supported by most Zip products, are the names and the locations of the parts of the file:

  • Each part of a split Zip file has a different extension (name.z01, name.z02, etc., and name.zip for the last part or name.zx01, name.zx02, etc., and name.zipx for the last part). All parts of a spanned Zip file have the same name (name.zip or name.zipx).
  • The parts of a split Zip file can all be located in the same folder; the parts of a spanned Zip file must reside on separate diskettes (because they all have the same name).

Internally, the parts of split Zip files and spanned Zip files are identical. Therefore, you can convert from one format to another if necessary by simply copying and renaming the parts as follows:

The ZipArchive Library can create segmented archives using the following methods: splitting, binary splitting and spanning.

  • splitting - an archive is split into multiple files that are usually located in the same directory. This method creates the same internal structure as spanning.
  • binary splitting - an archive file is logically a regular single-segment archive, but is binary split into multiple files. A regular archive can be created from such split archive by simply concatenating all its parts.
  • spanning - an archive spans multiple removable disks (e.g. floppy disks).

The differences between splitting and spanning are summarized below:

Splitting Spanning 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, (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

  • Splitting and spanning are compatible with PKZIP and WinZip.
  • Binary splitting is compatible e.g. with 7-Zip, but is not compatible with WinZip.
  • To set a callback object for splitting or spanning use the method.The class of the callback object must be derived from the CZipSegmCallback class.
  • The ZipArchive Library does not allow direct modifications of existing segmented archives. However you can apply changes to an existing segmented archive by creating a new archive and copying data from the old archive using one of the CZipArchive::GetFromArchive() methods. These methods will copy compressed data from the old archive without decompression. You can find more information about this method here: .
  • The CZipArchive class uses a write buffer to optimize the speed of write operations. You can change its size with the method (set the first argument). While creating a segmented archive, set the size of the buffer to the maximum size of the volume for the best performance.
  • To determine the total number of volumes in an archive, first request the central directory information using the method. The total number of volumes can be then obtained by adding one to the value, as illustrated in the sample code below. Sample Code CZipArchive zip; zip.Open(_T("C:\\Temp\\test.zip")); CZipCentralDir::CInfo info; zip.GetCentralDirInfo(info); ZIP_VOLUME_TYPE uTotalSegments = info.m_uLastVolume + 1; zip.Close();

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%.2u. 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 named 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').
  • The conversion is not possible in case of binary splitting.

Limits in Number of Volumes

Zip format has the following limits on the number of volumes:

Splitting Spanning Standard Zip Format 65,535 999 Zip64 Format4,294,967,295 - 1 4,294,967,295 - 1

  • The number of volumes in binary splitting depends on the type defined by ZIP_VOLUME_TYPE.
  • 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 in regular split. 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 instead. If the declared volume size is too small to hold an entire internal structure, this particular volume will be enlarged. It is recommended to use volumes sizes not smaller than 64KB.

Under Linux/OS X, when you are opening an existing split archive, use CZipArchive::zipOpenSplit mode when calling the method. This is caused by the lack of the implementation of the function 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::zipCreateSplit, 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 e.g. need to have the possibility to prompt a user for a location of a volume or perform some other actions.

When the callback is set, the method will be called every time a volume changes.

  • The reason for calling the callback is stored in and takes one of the values.
  • You can change the filename and path of the current volume by modifying the variable. When the callback is called, this variable holds the full path to the volume file as expected by the library. You can change this variable and the library will create the volume file under a new name or location. For implementing a custom naming scheme it is recommended to use the split names handler (see ) instead.
  • The number of the disk needed for reading or writing is stored in .
  • To abort the archive processing, return false from this method. A CZipException will be thrown with the 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 toZIP_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)

{

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;

}

}

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::zipCreateSplit, 1024 * 1024);

zip.AddNewFile(_T("C:\\Temp\\big.dat"));

zip.Close();

return;

zip.Open(zipFileName);

zip.ExtractFile(0, _T("C:\\Temp"), false, _T("big.ext"));

zip.Close();

}

Custom Naming Scheme of Volumes

You can implement a custom naming scheme of volumes for split archives. In order to do that:

  • Create a class derived from the CZipSplitNamesHandler class.
  • Implement the method.
  • For binary split, implement also the method. It should return 0 when the volume number could not be recognized.
  • Set the instance of the created class to be used for custom naming scheme with the or method before opening an archive. It needs to be done before every opening of an archive.

If the last volume name is different from the archive name, you can retrieve it when closing the archive (it is the return value of the method).

Sample Code

class CCustomNamesHandler : public CZipSplitNamesHandler

{

public:

CZipString GetVolumeName(const CZipString& archiveName,

ZIP_VOLUME_TYPE uCurrentVolume,

ZipArchiveLib::CBitFlag flags) const

{

CZipString szExt;

if (uCurrentVolume < 1000)

szExt.Format(_T("vol%.3u"), uCurrentVolume);

else

szExt.Format(_T("vol%u"), uCurrentVolume);

if (flags.IsSetAny(CZipSplitNamesHandler::flExisting))

{

CZipPathComponent zpc(archiveName);

zpc.SetExtension(szExt);

return zpc.GetFullPath();

}

else

{

return archiveName + _T(".") + szExt;

}

}

};

void CustomNaming()

{

LPCTSTR zipFileName = _T("C:\\Temp\\test.zip");

CZipArchive zip;

CCustomNamesHandler namesHandler;

zip.SetSplitNamesHandler(namesHandler);

zip.Open(zipFileName, CZipArchive::zipCreateSplit, 1024 * 1024);

zip.AddNewFile(_T("C:\\Temp\\big.dat"));

CZipString szLastVolumeName = zip.Close();

if (szLastVolumeName.IsEmpty())

{

_tprintf(_T("An unexpected error ocurred.\r\n"));

return;

}

zip.SetSplitNamesHandler(namesHandler);

zip.Open(szLastVolumeName);

zip.ExtractFile(0, _T("C:\\Temp"), false, _T("big.ext"));

zip.Close();

}

Binary Split

The binary splitting produces archives with the internal structure of a single-segment archive, but splits the archive into multiple files. Here is the comparison between the regular splitting and the binary splitting:

Regular Splitting Binary Spanning Internal Archive Structure Multi-segment. Each volume is logically represented inside of the archive. Single-segment archive. Volumes Extension Replaced with z%.2u pattern to create volume filenames (e.g. archive.z01). Consecutive numbers (`CZipArchive`4 pattern) are appended as an extension to an archive filename (e.g. archive.zip.001). Last Volume's Filename The same as the filename of the archive provided to the method (does not contain a volume number). The filename is formed as any other volume name (contains a volume number). Default Name Handler CZipRegularSplitNamesHandler CZipBinSplitNamesHandler Opening of Existing Archive The mode is automatically detected. You need to open the last volume. You need to specify when calling the method. You need to open the last volume.

Sample Code

CZipString zipFileName = _T("C:\\Temp\\test.zip");

CZipArchive zip;

zip.Open(zipFileName, CZipArchive::zipCreateBinSplit, 1024 * 1024);

zip.AddNewFile(_T("C:\\Temp\\big.dat"));

zipFileName = zip.Close();

if (zipFileName.IsEmpty())

{

_tprintf(_T("An unexpected error ocurred.\r\n"));

return;

}

zip.Open(zipFileName, CZipArchive::zipOpenBinSplit);

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 method). Setting the callback object is needed for creation as well as for extraction of spanned archives.
  • The method will be called every time a disk change is needed.
    • The reason for calling the callback is stored in and takes one of the values.
    • The value of the uProgress parameter of the callback method is the minimum number of free bytes required on the disk.
    • The number of the disk needed for reading or writing is stored in
      .  
    • To abort the archive processing, return false from this method. A CZipException will be thrown with the code.

Sample Code

include

class CSpanCallback : public CZipSegmCallback

{

bool Callback(ZIP_SIZE_TYPE)

{

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::zipCreateSpan);

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 Last Disk in 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 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 a user has not inserted the last disk into the drive.

Recovering from Invalid Disk Inserted

Invalid Last Disk

To recover from the situation when a user does not insert the last disk:

  • Catch the exception and verify its code. The code should be . Other codes may indicate a corrupted archive or file access problem.
  • Close the archive with the method, passing as the `CZipArchive`7 parameter.
  • Prompt the user for the last disk again.
  • Open the archive.
  • Repeat the process until the archive was successfully opened or the user cancelled the operation.

To recover from the situation when a user does not insert a correct disk during extraction:

  • Catch the exception.
  • Call the . This will also close any file opened for extraction.
  • Prompt the user for the correct disk.
  • Retry the extraction of the file from the beginning.

Callbacks Called

While processing a segmented archive the following callbacks that are called are the most important: