iPhone解压缩代码

26

我在尝试编写代码以在iPhone上解压缩文件或目录,但一直卡住了。

以下是我正在使用的一些示例代码,试图解压缩一个简单的文本文件。

它可以解压缩文件,但文件已经损坏。

(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);  
}
8个回答

41

我想要一个简单的解决方案,但在这里没有找到我喜欢的,所以我修改了一个库来实现我想要的功能。你可能会发现SSZipArchive很有用。(它也可以创建zip文件)

用法:

NSString *path = @"path_to_your_zip_file";
NSString *destination = @"path_to_the_folder_where_you_want_it_unzipped";
[SSZipArchive unzipFileAtPath:path toDestination:destination];

1
这是针对gzip还是winzip文件的? - whitneyland
3
这是我看过的最好的解决方案,做得很好! - T. Markle

13

确认 "sample.zip" 是否是由 gZip 创建的?通常以 .zip 作为扩展名的文件是由 WinZip 创建的压缩存档。虽然它们也可以使用 zLib 进行解压缩,但您需要解析头信息并使用其他程序。

要检查,请查看文件的前两个字节。如果是'PK',则是 WinZip 文件;如果是 0x1F8B,则是 gZip 文件。

由于这是 iPhone 特定的问题,可以参考这个 iPhone SDK 论坛讨论,其中提到了 miniZip。看起来这个库可以处理 WinZip 文件。

但如果它真的是 WinZip 文件,您应该查看 WinZip 规范 并尝试自己解析文件。基本上,您需要解析一些头部值,寻找压缩流的位置并使用 zLib 程序库对其进行解压缩。


1
我有一个问题...使用miniZip是否属于使用私有API、未记录API或非公共API?请帮忙。 - Pria
1
这样做是可以的 - 您可以使用与应用程序捆绑的库。私有/未记录的API问题通常仅涉及Apple的代码。 - Jay Peyer

12

对于gzip,这段代码对我很有用:

数据库的准备方式如下: gzip foo.db

关键在于循环使用gzread()。上面的示例仅读取前CHUNK个字节。

#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");

顺便提一下,我可以在77秒内将33MB解压缩成130MB,大约是每秒1.7MB的未压缩速度。


我之前在iOS上成功地使用了gzip,但是在我的最新项目中我需要提取多个文件,所以现在我将尝试使用SSZipArchive。 - neoneye

4
此代码将解压任何.zip文件到您的应用程序文档目录,并从应用程序资源中获取文件。
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];

2
@marshad13,'ZipArchive'类是从哪里来的?我在iOS和OSX文档中搜索,但无法找到该类。 - Anthony Kong

1

解压任意的zip文件真的很困难。这是一种复杂的文件格式,内部可能使用了许多不同的压缩算法。Info-ZIP有一些可自由许可的代码可以完成此任务(http://www.info-zip.org/UnZip.html),但需要进行一些修改才能在iPhone上运行,而且API非常糟糕——它需要将命令行参数传递给一个模拟UnZIP运行的假“main”(公平地说,这是因为他们的代码从一开始就没有被设计成像这样使用,库功能是事后添加的)。

如果您可以控制要解压缩的文件的来源,我强烈建议使用其他压缩系统而不是ZIP。ZIP的灵活性和普及性使其非常适合在人与人之间传递文件存档,但是自动化起来非常麻烦。


我不认为这很复杂。在大多数情况下,您将使用deflate生成一个标准的ZIP文件。在这种情况下,您只需要寻找正确的位置,然后可以使用zLib deflate,它看起来类似于发布的代码,只是稍微长一些并使用不同的例程。 - schnaader
只有在做出假设的情况下,它才不会很复杂,而且许多旧的ZIP文件并没有使用deflate算法。如果您可以保证所有要使用的文件都是正确类型的ZIP文件,那就没问题了。但是,如果您正在控制压缩,为什么要使用ZIP,除非这些文件也是给用户使用的呢? - th_in_gs

1

zlib并不是用来打开.zip文件的,但你很幸运:zlib的contrib目录包含了minizip,它能够使用zlib来打开.zip文件。

虽然可能没有在SDK中捆绑,但你可以使用捆绑版本的zlib。获取zlib源代码的副本,然后查看contrib/minizip。


0

我没有使用过iPhone,但你可能想看一下GZIP,这是一个非常便携的开源zip库,可用于许多平台。


-5

我在iPhone模拟器上进行了一些测试,运气不错:

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]);       
}

这看起来比折腾zlib要容易...


1
可以工作,但不幸的是不能在设备上运行。 - scud
15
system()...我希望这只是一个玩笑。 - Mike Weller
我认为这是一个非常好的解决方案,这不是开玩笑。我现在将测试这个解决方案。 - jackiszhp
系统在iOS上不可用。 - jackiszhp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接