C++ Zip Example to Append In-Memory Binary and String Data


void qa_create_zip_from_data(void)
{
CkZip zip;

zip.NewZip("qa_output/test.zip");

// This is the content of the files to be added to the .zip
const char *fileContents = "The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog.";

// AppendString2 returns a CkZipEntry, so make sure to get it and delete it if not needed..
// If NULL is returned, then the AppendString2 failed.
CkZipEntry *ent = zip.AppendString2("quickBrownFox1.txt",fileContents,"ansi");
if (ent) { delete ent; ent = 0; }

// Another way of adding in-memory data to a .zip is by calling AppendData. This is good for binary (non-text) data.
// In this case, we'll use the bytes of fileContents.
size_t szContent = strlen(fileContents);

CkByteData binaryContent;
// It is possible to let the CkByteData "borrow" data.  This avoid copying the bytes, which is good if the amount
// of data is large.
binaryContent.borrowData(fileContents,szContent);

// Add the binaryContent to the zip.
ent = zip.AppendData("quickBrownFox2.txt",binaryContent);
if (ent) { delete ent; ent = 0; }

// We now have a zip object (not yet a file on disk) that contains 2 entries: quickBrownFox1.txt and quickBrownFox2.txt
// Write the .zip to a file  (this is where the actual compression occurs)
// This writes the .zip to "qa_output/test.zip"
bool success = zip.WriteZipAndClose();
if (!success)
    {
    printf("%s\n",zip.lastErrorText());
    }
else
    {
    printf("success.\n");
    }
}

Unzipping zips with Unicode Filenames

Question:

I see your example code includes the ability to create a zip with Unicode filenames.
Does your product support unzipping files with unicode filenames, such as Chinese?

Answer:

Yes, it can unzip files w/ Unicode names — assuming the .zip was correctly created.  Your first test should be to unzip without trying anything differently.  Check to see if the files are created w/ the correct filenames.  If not, try setting the zip.OemCodePage property = 65001 (for utf-8), then re-try.  If that doesn’t work, it may be that the .zip was created such that the filenames are embedded using a specific charset.  You would need to set the OemCodePage property to match that charset.  See this: http://www.chilkatsoft.com/p/p_453.asp

(IOS/IPhone) Setting Zip.TempDir to the Documents Directory

The Chilkat Zip methods for writing a .zip will first write the zip to a temp file in the directory specified by the Zip.TempDir property, and then if successful, moved/renamed to the actual output file. The default value for zip.TempDir is “.” (the current working directory) and this is often a directory where it is not possible to create files. On IOS, a good solution is to set the zip.Temp directory equal to the app’s Documents directory, as shown in the code snippet below.

* The reason a temp file is used is because quite often the call to WriteZip / WriteZipAndClose is overwriting an existing .zip archive. If Chilkat Zip were to write the output file directly, and the zipping failed mid-way, then the existing .zip would be lost. Using the temp file eliminates this situation. The existing .zip is overwritten only when the entire new .zip is written.

* Also, depending on the situation and the sizes of files involved, temp files may be used when unzipping. The location of the temp directory for both zipping and unzipping is controlled by the TempDir property.

- (NSString *)documentsDirectory {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
           NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    return documentsDirectory;
}

...
...
CkoZip *zip = [[[CkoZip alloc] init] autorelease];
...
...
zip.TempDir = [documentsDirectory];

success = [zip WriteZipAndClose];
if (success != YES) {
    [strOutput appendString: zip.LastErrorText];
    [strOutput appendString: @"\n"];
    self.mainTextField.stringValue = strOutput;
    return;
}

...

Verify / Test a Zip Archive

Question:
I need an easy way to verify that a zip downloaded, i.e test it, preferably without actually unzipping it to a directory. I couldn’t find any method like that – the files are small enough in the zip I could probably unzip them into memory. Is that pretty much the only way to do it short of actually unzipping them?

Answer:
The Chilkat.Zip.OpenZip method can be used to verify a .zip. It walks the file headers and central directory and verifies that they are valid. It is not possible to verify the compressed data without actually decompressing every bit. For a good graphical picture of the zip file format (making it easy to understand) see the Wikipedia article on the Zip file format.

The OpenZip method returns false if there is an error (and also of course for other problems, such as “file not found”). It is possible to call OpenZip on an encrypted Zip without providing the password because the password is only required to decrypt the actual data. The file headers and central directory are still readable w/out a password.

Delete Files from a Zip Archive

Question:

“I’ve been trying out the ZIP package that you guys have and for the most part it’s great. I do however have a question about folder manipulation. What I would like to be able to do is individually remove files from the zip and if the directory is empty I would like to remove the directory from the zip file. Is there any way to remove directories from the zip as I can’t seem to find a way to do this? Also, is there any way to move/append files into a directory or create a directory in the zip?”

Answer:

The files contained within a .zip archive may or may not include a relative or absolute path.  For example, it is possible to have a .zip with several “text.txt” files:

  1. subdir1/test.txt
  2. test.txt
  3. subdir2/subdirA/test.txt

To delete one or more files from a .zip, iterate over the entries from 0 to zipObject.NumEntries-1.  For each ZipEntry object, your code can examine the entryObject.FileName property (which may also include a subdirectory path) to determine if it should be deleted.  If so, then call zipObject.DeleteEntry(entryObject) to remove the entry from the zipObject’s entries.  Once all entries have been deleted, call WriteZip or WriteZipAndClose to re-write the .zip with the deleted entries removed.

When iterating from 0 to zipObject.NumEntries, you will iterate over both file and directory entries.  The entryObject.IsDirectory property indicates whether the entry is a directory entry or not.  Removing directory entries is the same as for files.  (Note: Removing a directory entry does not remove all the files within that directory, it simply removes the explicit directory entry within the .zip)

To create a new explicit directory entry within a .zip, call zipObject.AppendNewDir.  (Note: All methods that Append files/dirs do not affect the existing .zip until WriteZip or WriteZipAndClose is called.)

To “move” files into a sub-directory within the .zip, simply update the zipEntry.FileName property to include the new subdirectory.  For example, change the zipEntry.FileName property from “test.txt” to “subdir1.test.txt”, then rewrite the .zip.  (Don’t re-write the .zip after each zipEntry object is modified.  Make all modifications (appends, deletes, zipEntry.FileName changes, etc.) and the rewrite with a single call to WriteZip/WriteZipAndClose.

How to Add Comment to a Zip Entry?

Question: How do I go about adding text to the entry.comment field while zipping individual files?

Answer: After appending files via the AppendFiles (or other append methods), you’ll have a zip object with N entries, each of which is a reference to a file that will be zipped when WriteZip is called (or WriteZipAndClose, or WriteExe, etc.).
You can then iterate over the entries, or search for a particular entry, and then update that entry’s Comment property with the comment you want to add.  Then when you write the zip, your comment is included…

Zip DecryptPassword and EncryptPassword

Question:

I’m confused about DecryptPassword and EncryptPassword.

I want to encrypt the files I am emailing. I want the recipient to be required to enter a password when they try to open the zipped file. Do I EncryptPassword or DecryptPassword?

Answer:

Use DecryptPassword when using the Chilkat Zip component / library to open an encrypted zip. Use EncryptPassword when writing an encrypted .zip. The reason there are two separate properties is because of this situation: Let’s say you want to rewrite an encrypted .zip using a new password. Chilkat Zip will open the existing zip and decrypt using DecryptPassword, but will use EncryptPassword when rewriting the zip.

VB.NET to Extract a File from one Zip Archive and Append it to Another Zip Archive

The following VB.NET code copies the contents of the nutrition.xml file (found in xml1.zip) to another pre-existing .zip named xml2.zip:

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim zip1 As New Chilkat.Zip
        Dim success As Boolean

        success = zip1.UnlockComponent("Anything for 30-day trial")

        success = zip1.OpenZip("xml1.zip")

        Dim entry As Chilkat.ZipEntry
        entry = zip1.GetEntryByName("nutrition.xml")
        ' Assume the entry was found and is non-null

        ' Now we'll add this entry to a different already-existing zip archive.
        ' To avoid re-writing the entire zip, we use QuickAppend.
        ' In this example, the already-existing .zip is "xml2.zip"

        ' Create a new zip object for the purpose of holding the entries that will
        ' be appended to xml2.zip
        Dim zipTemp As New Chilkat.Zip
        zipTemp.NewZip("thisIsADummyZipObjectAndIsNeverWritten.zip")

        ' Retrieve the compressed data from the entry object and add it to our
        ' temporary zip object.
        zipTemp.AppendCompressed("nutrition.xml", entry.Copy())

        ' Append all the entries in zipTemp to the existing zip file named "xml2.zip"
        ' This writes the entries in zipTemp (all in memory) to xml2.zip
        success = zipTemp.QuickAppend("xml2.zip")


    End Sub

Zip Compress JPG?

Question:

May I know that which Zip method in your component could give the best compression?
We are trying to Zip some JPEG, and perhaps could reduce the size by half or more.

Answer:

The JPG file format is already a compressed format, so usually very little is gained in trying to compress it — the result is a lot of CPU cycles/time spent with minimal or no gain.  Therefore, the Chilkat Zip component automatically avoids compressing JPG files.  You may force the Zip component to compress JPG’s by calling Chilkat.Zip.RemoveNoCompressExtension(“.jpg”)
See: http://www.chilkatsoft.com/refdoc/csZipRef.html