48

Is there a simple example of how to unzip a .zip file and extract the files to a directory? I am currently using zlib, and while I understand that zlib does not directly deal with zip files, there seems to be several additional things in zlibs's "contrib" library. I noticed and read about "minizip", and after reading some documents and looking at some of the code, I do not see a simple example of how to unzip a .zip file and extract the files to a directory.

I would like to find a platform independent way of doing so, but if that is not possible then I need to find a way for windows and mac.

judeclarke
  • 1,106
  • 5
  • 16
  • 31
  • 4
    For readers looking for how to do the opposite - to create a zip file using zlib, see my answer here: http://stackoverflow.com/questions/11370908/how-do-i-use-minizip-on-zlib – niemiro Sep 09 '14 at 11:49

2 Answers2

60

zlib handles the deflate compression/decompression algorithm, but there is more than that in a ZIP file.

You can try libzip. It is free, portable and easy to use.

UPDATE: Here I attach quick'n'dirty example of libzip, with all the error controls ommited:

#include <zip.h>

int main()
{
    //Open the ZIP archive
    int err = 0;
    zip *z = zip_open("foo.zip", 0, &err);

    //Search for the file of given name
    const char *name = "file.txt";
    struct zip_stat st;
    zip_stat_init(&st);
    zip_stat(z, name, 0, &st);

    //Alloc memory for its uncompressed contents
    char *contents = new char[st.size];

    //Read the compressed file
    zip_file *f = zip_fopen(z, name, 0);
    zip_fread(f, contents, st.size);
    zip_fclose(f);

    //And close the archive
    zip_close(z);

    //Do something with the contents
    //delete allocated memory
    delete[] contents;
}
rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • 3
    Could you please provide an simple example of how to unzipped a file using libzip? – judeclarke May 03 '12 at 22:41
  • after days of wrestling with libzip to get it to compile on windows, it turns out the code in your post does not compile. zip_stat is a function, not a type like you mentioned – judeclarke May 10 '12 at 17:36
  • 1
    @judeclarke - Actually it is both! In C there is no ambiguity, but in C++ there is. Just change `zip_stat st;` into `struct zip_stat st;` and done. – rodrigo May 10 '12 at 19:11
  • 1
    it gives 'undefined reference to functions' when i try to compile it with g++ o ubuntu – aceBox Jan 30 '14 at 23:22
  • @aceBox: Did you add the `-lzip` option to the compiler command? – rodrigo Jan 31 '14 at 07:27
35

Minizip does have an example programs to demonstrate its usage - the files are called minizip.c and miniunz.c.

Update: I had a few minutes so I whipped up this quick, bare bones example for you. It's very smelly C, and I wouldn't use it without major improvements. Hopefully it's enough to get you going for now.

// uzip.c - Simple example of using the minizip API.
// Do not use this code as is! It is educational only, and probably
// riddled with errors and leaks!
#include <stdio.h>
#include <string.h>

#include "unzip.h"

#define dir_delimter '/'
#define MAX_FILENAME 512
#define READ_SIZE 8192

int main( int argc, char **argv )
{
    if ( argc < 2 )
    {
        printf( "usage:\n%s {file to unzip}\n", argv[ 0 ] );
        return -1;
    }

    // Open the zip file
    unzFile *zipfile = unzOpen( argv[ 1 ] );
    if ( zipfile == NULL )
    {
        printf( "%s: not found\n" );
        return -1;
    }

    // Get info about the zip file
    unz_global_info global_info;
    if ( unzGetGlobalInfo( zipfile, &global_info ) != UNZ_OK )
    {
        printf( "could not read file global info\n" );
        unzClose( zipfile );
        return -1;
    }

    // Buffer to hold data read from the zip file.
    char read_buffer[ READ_SIZE ];

    // Loop to extract all files
    uLong i;
    for ( i = 0; i < global_info.number_entry; ++i )
    {
        // Get info about current file.
        unz_file_info file_info;
        char filename[ MAX_FILENAME ];
        if ( unzGetCurrentFileInfo(
            zipfile,
            &file_info,
            filename,
            MAX_FILENAME,
            NULL, 0, NULL, 0 ) != UNZ_OK )
        {
            printf( "could not read file info\n" );
            unzClose( zipfile );
            return -1;
        }

        // Check if this entry is a directory or file.
        const size_t filename_length = strlen( filename );
        if ( filename[ filename_length-1 ] == dir_delimter )
        {
            // Entry is a directory, so create it.
            printf( "dir:%s\n", filename );
            mkdir( filename );
        }
        else
        {
            // Entry is a file, so extract it.
            printf( "file:%s\n", filename );
            if ( unzOpenCurrentFile( zipfile ) != UNZ_OK )
            {
                printf( "could not open file\n" );
                unzClose( zipfile );
                return -1;
            }

            // Open a file to write out the data.
            FILE *out = fopen( filename, "wb" );
            if ( out == NULL )
            {
                printf( "could not open destination file\n" );
                unzCloseCurrentFile( zipfile );
                unzClose( zipfile );
                return -1;
            }

            int error = UNZ_OK;
            do    
            {
                error = unzReadCurrentFile( zipfile, read_buffer, READ_SIZE );
                if ( error < 0 )
                {
                    printf( "error %d\n", error );
                    unzCloseCurrentFile( zipfile );
                    unzClose( zipfile );
                    return -1;
                }

                // Write data to file.
                if ( error > 0 )
                {
                    fwrite( read_buffer, error, 1, out ); // You should check return of fwrite...
                }
            } while ( error > 0 );

            fclose( out );
        }

        unzCloseCurrentFile( zipfile );

        // Go the the next entry listed in the zip file.
        if ( ( i+1 ) < global_info.number_entry )
        {
            if ( unzGoToNextFile( zipfile ) != UNZ_OK )
            {
                printf( "cound not read next file\n" );
                unzClose( zipfile );
                return -1;
            }
        }
    }

    unzClose( zipfile );

    return 0;
}

I built and tested it with MinGW/MSYS on Windows like this:

contrib/minizip/$ gcc -I../.. -o unzip uzip.c unzip.c ioapi.c ../../libz.a
contrib/minizip/$ ./unzip.exe /j/zlib-125.zip
x-x
  • 7,287
  • 9
  • 51
  • 78
  • I looked into that example previously and that is a huge example. is there a simpler example you could provide? – judeclarke May 03 '12 at 22:39
  • @judeclarke, I'm away from my dev box for the day, but I'll post something smaller/simpler when I can! – x-x May 03 '12 at 22:49
  • 6
    You simply need to take the time to read the header files and the examples. First look at zip.h and unzip.h to see what the functions are that are provided and what they do. Then look at minizip.c and miniunz.c to see how they are used. – Mark Adler May 04 '12 at 00:00
  • With unzReadCurrentFile is it not possible to have 2 "open" files within the zip? – paulm May 26 '16 at 12:45
  • @paulm: just create two instances of unzFile – Paul Sanders Apr 13 '18 at 08:53
  • fails on windows as the file is opened with exclusive access – paulm Apr 14 '18 at 17:21
  • unzFile is already a pointer. It must be used as `unzFile zipfile = unzOpen( argv[ 1 ] );` – Cem Polat Oct 15 '21 at 07:40