26

Really stuck on trying to write code to unzip a file or directory on the iPhone.

Below is some sample code that I'm using to try and unzip a simple text file.

It unzips the file but its corrupt.

(void)loadView {

    NSString *DOCUMENTS_FOLDER = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
    NSString *path = [DOCUMENTS_FOLDER stringByAppendingPathComponent:@"sample.zip"];

    NSString *unzipeddest = [DOCUMENTS_FOLDER stringByAppendingPathComponent:@"test.txt"];  

    gzFile file = gzopen([path UTF8String], "rb");

    FILE *dest = fopen([unzipeddest UTF8String], "w");

    unsigned char buffer[CHUNK];

    int uncompressedLength = gzread(file, buffer, CHUNK);

    if(fwrite(buffer, 1, uncompressedLength, dest) != uncompressedLength ||     ferror(dest)) {
        NSLog(@"error writing data");
    }
    else{

    }

    fclose(dest);
    gzclose(file);  
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
TonyNeallon
  • 6,607
  • 12
  • 42
  • 49

8 Answers8

41

I wanted an easy solution and didn't find one I liked here, so I modified a library to do what I wanted. You may find SSZipArchive useful. (It can also create zip files by the way.)

Usage:

NSString *path = @"path_to_your_zip_file";
NSString *destination = @"path_to_the_folder_where_you_want_it_unzipped";
[SSZipArchive unzipFileAtPath:path toDestination:destination];
Cœur
  • 37,241
  • 25
  • 195
  • 267
Sam Soffes
  • 14,831
  • 9
  • 76
  • 80
13

Has "sample.zip" really been created with gZip? The .zip extension usually is used for archives created by WinZip. Those can also be decompressed using zLib, but you'd have to parse the header and use other routines.

To check, have a look at the first two bytes of the file. If it is 'PK', it's WinZip, if it's 0x1F8B, it's gZip.

Because this is iPhone specific, have a look at this iPhone SDK forum discussion where miniZip is mentioned. It seems this can handle WinZip files.

But if it's really a WinZip file, you should have a look at the WinZip specification and try to parse the file yourself. It basically should be parsing some header values, seeking the compressed stream position and using zLib routines to decompress it.

schnaader
  • 49,103
  • 10
  • 104
  • 136
  • 1
    I have a query...whether using miniZip will come under using private API or undocumented API or non-public API? Please help. – Pria Sep 28 '10 at 15:20
  • 1
    Doing this is fine -- you can use libraries that you bundle with the app yourself. The private/undocumented API issue is typically just around Apple's code. – Jay Peyer Mar 16 '11 at 14:34
12

This code worked well for me for gzip:

the database was prepared like this: gzip foo.db

the key was looping over the gzread(). The example above only reads the first CHUNK bytes.

#import <zlib.h>
#define CHUNK 16384


  NSLog(@"testing unzip of database");
  start = [NSDate date];
  NSString *zippedDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"foo.db.gz"];
  NSString *unzippedDBPath = [documentsDirectory stringByAppendingPathComponent:@"foo2.db"];
  gzFile file = gzopen([zippedDBPath UTF8String], "rb");
  FILE *dest = fopen([unzippedDBPath UTF8String], "w");
  unsigned char buffer[CHUNK];
  int uncompressedLength;
  while (uncompressedLength = gzread(file, buffer, CHUNK) ) {
    // got data out of our file
    if(fwrite(buffer, 1, uncompressedLength, dest) != uncompressedLength || ferror(dest)) {
      NSLog(@"error writing data");
    }
  }
  fclose(dest);
  gzclose(file);
  NSLog(@"Finished unzipping database");

Incidentally, I can unzip 33MB into 130MB in 77 seconds or about 1.7 MB uncompressed/second.

Carl Coryell-Martin
  • 3,410
  • 3
  • 26
  • 23
  • I have used gzip successfully on iOS, but with my latest project I need to extract multiple file, so I will now explore SSZipArchive. – neoneye May 05 '11 at 09:11
4

This code will unzip any .zip file into your app document directory and get file from app resources.

self.fileManager = [NSFileManager defaultManager];

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSLog(@"document directory path:%@",paths);

self.documentDirectory = [paths objectAtIndex:0];

NSString *filePath = [NSString stringWithFormat:@"%@/abc", self.documentDirectory];

NSLog(@"file path is:%@",filePath);

NSString *fileContent = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"data.zip"];


NSData *unzipData = [NSData dataWithContentsOfFile:fileContent];

[self.fileManager createFileAtPath:filePath contents:unzipData attributes:nil];

// here we go, unzipping code

ZipArchive *zipArchive = [[ZipArchive alloc] init];

if ([zipArchive UnzipOpenFile:filePath])
{
    if ([zipArchive UnzipFileTo:self.documentDirectory overWrite:NO])
    {
        NSLog(@"Archive unzip success");
        [self.fileManager removeItemAtPath:filePath error:NULL];
    }
    else
    {
        NSLog(@"Failure to unzip archive");
    }
}
else
{
    NSLog(@"Failure to open archive");
}
[zipArchive release];
mahboudz
  • 39,196
  • 16
  • 97
  • 124
marshad13
  • 41
  • 1
  • 2
    @marshad13 Where does the class 'ZipArchive' come from? I searched the ios and osx documentation but I am unable to locate the class – Anthony Kong May 26 '13 at 11:10
1

It's really hard to unzip any arbitrary zip file. It's a complex file format, and there are potentially many different compression routines that could have been used internally to the file. Info-ZIP has some freely licencable code to do it (http://www.info-zip.org/UnZip.html) that can be made to work on the iPhone with some hacking, but the API is frankly horrible - it involves passing command-line arguments to a fake 'main' that simulates the running of UnZIP (to be fair that's because their code was never designed to be used like this in the first place, the library functionality was bolted on afterwards).

If you have any control of where the files you're trying to unzip are coming from, I highly recommend using another compression system instead of ZIP. It's flexibility and ubiquity make it great for passing archives of files around in person-to-person, but it's very awkward to automate.

th_in_gs
  • 539
  • 4
  • 15
  • I'd not agree that it's that complicated. In most cases, you'll have a standard ZIP file using deflate. In this case, you'll only have to seek to the correct position and can use zLib deflate which would look similar to the posted code, only slightly longer and using different routines. – schnaader Jan 05 '09 at 13:12
  • It's only not complex if you make assumptions, and many older ZIP files in the wild don't use deflate. If you can guarantee all the files you'll be using are of the right 'kind' of ZIP, that's okay. If you're controlling the compression though, why use ZIP unless the files are also for the user? – th_in_gs Jan 06 '09 at 10:26
1

zlib isn't meant to open .zip files, but you are in luck: zlib's contrib directory includes minizip, which is able to use zlib to open .zip files.

It may not be bundled in the SDK, but you can probably use the bundled version of zlib use it. Grab a copy of the zlib source and look in contrib/minizip.

Jeff M
  • 700
  • 3
  • 10
0

I haven't used the iPhone, but you may want to look at GZIP, which is a very portable open source zip library available for many platforms.

SmacL
  • 22,555
  • 12
  • 95
  • 149
-5

I had some luck testing this on the iPhone simulator:

NSArray *paths = 
   NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *documentsDirectory = [paths objectAtIndex:0];

NSString *saveLocation = 
   [documentsDirectory stringByAppendingString:@"myfile.zip"];

NSFileManager* fileManager = [NSFileManager defaultManager];

if ([fileManager fileExistsAtPath:saveLocation]) {
    [fileManager removeItemAtPath:saveLocation error:nil];

}

NSURLRequest *theRequest = 
             [NSURLRequest requestWithURL:
                [NSURL URLWithString:@"http://example.com/myfile.zip"]
             cachePolicy:NSURLRequestUseProtocolCachePolicy
             timeoutInterval:60.0];    

NSData *received = 
             [NSURLConnection sendSynchronousRequest:theRequest 
                              returningResponse:nil error:nil];    

if ([received writeToFile:saveLocation atomically:TRUE]) {      
    NSString *cmd = 
       [NSString stringWithFormat:@"unzip \"%@\" -d\"%@\"", 
       saveLocation, documentsDirectory];       

    // Here comes the magic...
    system([cmd UTF8String]);       
}

It looks easier than fiddling about with zlib...