16

I'm trying to archive files for a cross-platform application, and it looks like Minizip (built on zlib) is about as portable as archivers come.

When I try to run the following dummy code, however, I get a system error [my executable] has stopped working. Windows can check online for a solution to the problem.

Can anyone help me see how to use this library? — (there's no doc or tutorial anywhere that I can find)

zip_fileinfo zfi;

int main()
{
    zipFile zf = zipOpen("myarch.zip",APPEND_STATUS_ADDINZIP);
    int ret = zipOpenNewFileInZip(zf,
        "myfile.txt",
        &zfi,
        NULL,   0,
        NULL,   0,
        "my comment for this interior file",
        Z_DEFLATED,
        Z_NO_COMPRESSION
        );
    zipCloseFileInZip(zf);
    zipClose(zf, "my comment for exterior file");
    return 0;
}

Specs: Msys + MinGW, Windows 7, using zlibwapi.dll from zlib125dll.zip/dll32

JellicleCat
  • 28,480
  • 24
  • 109
  • 162

3 Answers3

23

Since I found this question via Google and it didn't contain any complete, working code, I am providing some here for future visitors.

int CreateZipFile (std::vector<wstring> paths)
{
    zipFile zf = zipOpen(std::string(destinationPath.begin(), destinationPath.end()).c_str(), APPEND_STATUS_CREATE);
    if (zf == NULL)
        return 1;

    bool _return = true;
    for (size_t i = 0; i < paths.size(); i++)
    {
        std::fstream file(paths[i].c_str(), std::ios::binary | std::ios::in);
        if (file.is_open())
        {
            file.seekg(0, std::ios::end);
            long size = file.tellg();
            file.seekg(0, std::ios::beg);

            std::vector<char> buffer(size);
            if (size == 0 || file.read(&buffer[0], size))
            {
                zip_fileinfo zfi = { 0 };
                std::wstring fileName = paths[i].substr(paths[i].rfind('\\')+1);

                if (S_OK == zipOpenNewFileInZip(zf, std::string(fileName.begin(), fileName.end()).c_str(), &zfi, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION))
                {
                    if (zipWriteInFileInZip(zf, size == 0 ? "" : &buffer[0], size))
                        _return = false;

                    if (zipCloseFileInZip(zf))
                        _return = false;

                    file.close();
                    continue;
                }
            }
            file.close();
        }
        _return = false;
    }

    if (zipClose(zf, NULL))
        return 3;

    if (!_return)
        return 4;
    return S_OK;
}
niemiro
  • 1,778
  • 1
  • 20
  • 37
  • 1
    This is good. A few issues though: 1. This assumes that the entire file can fit in memory, which is somewhat a simplistic view, but perhaps good enough for this sample. 2. There's no reason why a 0 length file output is an error if the initial file length is 0. 3. Assuming that it is an error, there's no need for the ternary operation. i.e. `if (zipWriteInFileInZip(zf, size == 0 ? "" : &buffer[0], size))` can be written as `if (zipWriteInFileInZip(zf, &buffer[0], size))`. 4. I'd recommend not using an underscore prefix. I know you are using a lowercase letter after it, but... *shrug* – Adrian Mar 23 '16 at 17:05
  • The second and third points you raise are wrong. The size==0 check is to ensure that buffer[0] isn't executed on zero length. The logical or operation in the if statement then ensures that zero length files are written to the zip file correctly, and the later ternary is, once again, to ensure that buffer[0] isn't called on zero length. The code works correctly and as expected. – niemiro Jun 10 '16 at 00:35
  • 2
    This is tagged as C, not C++. – Neil Roy Apr 14 '17 at 01:55
  • There are some typos. Instead of `S_OK` (used in COM) one should write `Z_OK`. – Yogurt Aug 27 '20 at 18:42
  • 2
    @Yogurt Actually should be ZIP_OK or UNZ_OK from zip.h or unzip.h respectively. – RobinP Jul 01 '22 at 10:14
4

The minizip library does come with examples; minizip.c for zipping and miniunz.c for unzipping. Both are command line utilities that show how to use the library. They are a mess though.

You also need to fill the zfi zip_fileinfo. At the very least you should initialize the structure to zero. zfi contains information about the file you want to store using zipOpenNewFileInZip. The structure should contain the date and attributes of "myfile.txt".

I recommend using PKWARE Desktop to diagnosis zip issues. It shows the structure/properties of the files in the ZIP and the ZIP file itself. When I opened the myarch.zip it told me there were errors. I drilled down into the file properties and found that the attributes were off.

Nathan Moinvaziri
  • 5,506
  • 4
  • 29
  • 30
  • 1
    Thanks for the investigation, but in the code sample above, shouldn't `zfi` be initialized to zero without me specifying it? It's on the heap since it's outside of any function, right? – JellicleCat Jul 09 '12 at 14:09
  • Ah yes, you are correct. I just threw your code into a function to see if it worked. – Nathan Moinvaziri Jul 09 '12 at 17:18
  • 1
    Your code should work properly then. I haven't gotten any Windows crashes from it. It could be an issue with using the DLL. Have you tried compiling in the zlib library? – Nathan Moinvaziri Jul 09 '12 at 17:22
  • zfi is not on the heap, which is dynamically allocated memory such as obtained by malloc. On *NIX, zfi is in the BSS, which is indeed initialized to zero. Conceptually, all the BSS segment in an executable file specifies is its load address and length. – Mike Crawford Aug 30 '16 at 03:58
2

The minizip lib is well documented. Just open the zip.h for details.

I can tell you here, you may have passed a wrong parameter for zipOpen. (APPEND_STATUS_ADDINZIP requires an existing zip file!)

Also, please check whether zipOpen returns a valid zipFile handle.

Rango
  • 1,087
  • 7
  • 5
  • Thanks. I changed my 2nd arg to `APPEND_STATUS_CREATE`. I don't see any reference to validity in zip.h; for that, should I just check whether the zipfile is `NULL`? – JellicleCat Jul 07 '12 at 17:32
  • Additionally, can you shed any light on what `extrafield_local` and `extrafield_global` are supposed to be? I'm not finding any example or sufficient description. – JellicleCat Jul 07 '12 at 18:49
  • I never used extrafield_local and extrafield_global before. Sorry I can't help. If your zip.h contains no documentation, you can download zlib source code. I'm sure zip.h in that package contains good documentation. – Rango Jul 08 '12 at 11:56
  • 1
    Yes, you can check to see if zf is NULL to determine if it is a valid zipFile handle. – Nathan Moinvaziri Jul 09 '12 at 09:53
  • Extra field information is documented in the PKWARE ZIP specification appnote (http://www.pkware.com/documents/casestudies/APPNOTE.TXT). It basically allows you to store and retrieve any custom data you might want to with each file during the zipping/unzipping process. In your code above, you are passing in the correct values if you plan on not utilizing the extra field option. – Nathan Moinvaziri Jul 09 '12 at 09:58
  • 2
    I'm sorry to say this but minizip is far from anything close to being well documented. It's the absolute opposite in fact. – Javier Quevedo Nov 02 '16 at 12:05
  • And then off course the methods look like this: zipOpenNewFileInZip3_64. Don't worry, you also have zipOpenNewFileInZip3 or zipOpenNewFileInZip4. That's what I call a well defined API. – Javier Quevedo Nov 02 '16 at 12:12