Friday 13 May 2011

Zipping File Programmatically


Introduction

This is a simple class for compressing and extracting files. It works depend on minizip, which is a open source zip format library. And it’s included in the attachment.
The major class name is ZipArchive, it’s easy to use, you can declare a instance and call initialize functions, and then call addFileToZip or UnzipFileTo to finish compression or uncompression.

Details:

Usage: Add all the files to you project, and and framework libz.1.2.3.dylib.
include ZipArchive.h using #import "ZipArchive/ZipArchive.h"
To create and add files to a zip

BOOL ret = [zip CreateZipFile2:l_zipfile];
// OR
BOOL ret = [zip CreateZipFile2:l_zipfile Password:@"your password"]; //
//if the Password is empty, will get the same effect as [zip CreateZipFile2:l_zipfile];

ret = [zip addFileToZip:l_photo newname:@"photo.jpg"];
if( ![zip CloseZipFile2] )
{
// error handler here
}
[zip release];

Extract files in a zip file to special directory, if the directory does not exist, the class will create it automatically. also if you pass ‘overWrite’ as ‘YES’ it will overwrite files already exist. You can also implement the methods of ZipArchiveDelegate to give more choices for overwriting.

ZipArchive* za = [[ZipArchive alloc] init];
if( [za UnzipOpenFile:@"/Volumes/data/testfolder/Archive.zip"] )
// OR
if( [za UnzipOpenFile:@"/Volumes/data/testfolder/Archive.zip"] Password:@"password of the zip file"] )
// if the Password is empty, get same effect as if( [za UnzipOpenFile:@"/Volumes/data/testfolder/Archive.zip"] )
{
BOOL ret = [za UnzipFileTo:@"/Volumes/data/testfolder/extract" overWrite:YES];
if( NO==ret )
{
// error handler here
}
[za UnzipCloseFile];
}

[za release];

or
ZipArchive *za = [[ZipArchive alloc] init];
if ([za UnzipOpenFile: @"/Volumes/data/testfolder/Archive.zip"]) {
BOOL ret = [za UnzipFileTo: @"/Volumes/data/testfolde/extract" overWrite: YES];
if (NO == ret){} [za UnzipCloseFile];
}
[za release];


If you wish to add a function to merely list the items in the zip file, and return this as an NSString array, insert the following lines into ZipArchive.mm:
- (NSMutableArray *) getZipFileContents
{
BOOL success = YES;
int ret = unzGoToFirstFile( _unzFile );
NSMutableArray * AllFilenames = [[NSMutableArray alloc] initWithCapacity:40];

if( ret!=UNZ_OK )
{
[self OutputErrorMessage:@"Failed"];
}

do{
if( [_password length]==0 )
ret = unzOpenCurrentFile( _unzFile );
else
ret = unzOpenCurrentFilePassword( _unzFile, [_password cStringUsingEncoding:NSASCIIStringEncoding] );
if( ret!=UNZ_OK )
{
[self OutputErrorMessage:@"Error occured"];
success = NO;
break;
}

// reading data and write to file
unz_file_info fileInfo ={0};
ret = unzGetCurrentFileInfo(_unzFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
if( ret!=UNZ_OK )
{
[self OutputErrorMessage:@"Error occurs while getting file info"];
success = NO;
unzCloseCurrentFile( _unzFile );
break;
}
char* filename = (char*) malloc( fileInfo.size_filename +1 );
unzGetCurrentFileInfo(_unzFile, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0);
filename[fileInfo.size_filename] = '\0';

// check if it contains directory
NSString * strPath = [NSString stringWithCString:filename];
BOOL isDirectory = NO;
if( filename[fileInfo.size_filename-1]=='/' || filename[fileInfo.size_filename-1]=='\\')
isDirectory = YES;
free( filename );
if( [strPath rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"/\\"]].location!=NSNotFound )
{// contains a path
strPath = [strPath stringByReplacingOccurrencesOfString:@"\\" withString:@"/"];
}

// Copy name to array
[AllFilenames addObject:[strPath copy]];

unzCloseCurrentFile( _unzFile );
ret = unzGoToNextFile( _unzFile );
} while( ret==UNZ_OK && UNZ_OK!=UNZ_END_OF_LIST_OF_FILE );

return [AllFilenames autorelease];
}

  I added a new function to unzip the files in memory (NSData), instead of unzipping to files.
I use this library to handle a lot of small files, and it seemed that writing to temporary files was a bit of overhead. This function returns an array of dictionaries which include the file name and data for each file in the zip (directories are ignored)
-(NSArray*) UnzipFileToData
{
NSMutableArray *array = [NSMutableArray arrayWithCapacity:5];

BOOL success = YES;
int ret = unzGoToFirstFile( _unzFile );
unsigned char buffer[4096] = {0};

if( ret!=UNZ_OK )
{
[self OutputErrorMessage:@"Failed"];
return nil;
}

do{
if( [_password length]==0 )
ret = unzOpenCurrentFile( _unzFile );
else
ret = unzOpenCurrentFilePassword( _unzFile, [_password cStringUsingEncoding:NSASCIIStringEncoding] );
if( ret!=UNZ_OK )
{
[self OutputErrorMessage:@"Error occurs"];
success = NO;
break;
}
// reading data and write to file
int read ;
unz_file_info fileInfo ={0};
ret = unzGetCurrentFileInfo(_unzFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
if( ret!=UNZ_OK )
{
[self OutputErrorMessage:@"Error occurs while getting file info"];
success = NO;
unzCloseCurrentFile( _unzFile );
break;
}
char* filename = (char*) malloc( fileInfo.size_filename +1 );
unzGetCurrentFileInfo(_unzFile, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0);
filename[fileInfo.size_filename] = '\0';

// check if it contains directory
NSString * strPath = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding];


BOOL isDirectory = NO;
if( filename[fileInfo.size_filename-1]=='/' || filename[fileInfo.size_filename-1]=='\\')
isDirectory = YES;
free( filename );


if( isDirectory )
{
// Nothig to do with directories
unzCloseCurrentFile( _unzFile );
ret = unzGoToNextFile( _unzFile );
continue;
}

NSMutableData *data = [[NSMutableData alloc] init];
while( 1 )
{
read=unzReadCurrentFile(_unzFile, buffer, 4096);
if( read > 0 )
{
[data appendBytes:buffer length:read];
}
else if( read<0 )
{
[self OutputErrorMessage:@"Failed to read zip file"];
break;
}
else
break;
}
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:data,@"data", strPath, @"filename",nil];

[array addObject:dict];
unzCloseCurrentFile( _unzFile );
ret = unzGoToNextFile( _unzFile );

}
while( ret==UNZ_OK && UNZ_OK!=UNZ_END_OF_LIST_OF_FILE );
return success==NO?nil:[NSArray arrayWithArray:array];

}

No comments:

Post a Comment