用ZipArchive在App里压缩、解压文件

时间:2024-02-26 07:46:51

在这篇教程里,我将演示如何在iOS应用内压缩和解压文件。我们会使用到一个叫做ZipArchive的第三方库来实现这一功能。虽然还有其他压缩和解压文件的方案,但我感觉ZipArchive是最快和最易使用的。

为什么我需要解压文件?

这个问题很好。有很多理由可以用来解释为什么你的应用需要支持压缩和解压文件,这是其中一些:

苹果的50M下载限制

苹果强制要求通过3G网络下载的应用大小不能超过50M,绕过这个限制的方法是尽量让你的二进制包足够小,然后在应用里下载需要的资源,当然最好的方式是先把资源打包成一个zip文件。完整的步骤(下边会有演示)是:打开App,检测是否有资源更新、下载zip文件、解压zip文件。这样处理的话你就可以发布容量更小的应用,然后动态的获取资源。

动态更新内容

我碰到过这个问题。当你的应用需要更新资源,通常是发布一个新的包到App Store,但这可能需要花一周时间让苹果审核。更快的方式是把资源打包并发布到某个服务器(甚至是你的Dropbox),然后通过你的应用下载并解压这些资源。通过这种方式,无论何时你想发布只修改资源的更新,都不需要向App Store发布一个新版本。

从Web下载Zip文件

Safari和Mail一个很大的失败之处是不能打开zip文件,如果能够支持在移动设备上查看zip文档那会是非常棒的。很多从商店里下载的应用都支持这个功能,你也可以用ZipArchive来实现这样的功能。

设置你的工程

http://code.google.com/p/ziparchive/下载一份ZipArchive的拷贝,或者你可以在终端输入下边这条命令获取:

svn checkout http://ziparchive.googlecode.com/svn/trunk/ziparchive-read-only

获取到了代码后,从minizip目录删除MakeFile文件。我们会让Xcode编译这些文件。拖动minizip目录、ZipArchive.h和ZipArchive.mm到你的工程,确保选中“Create groups for any added folders”以及“targets”。

zip_01

注意:不支持ARC

如果你在项目中使用ARC,你需要告诉编译器不要为ZipArchive使用ARC。要配置这个请点击工程左边的列表,然后点击你的target并选择“Build Phases”标签。

展开“Compile Sources”,找到ZipArchive.mm并双击,在弹出的框中输入“-fno-objc-arc”并点击Done。

zip_02

连接libz

最后一步是和libz.1.2.5.dylib连接,在Build Phases选项卡中展开Link Binary With Libraries节,并点击“+”按钮来添加新的库。从列表里找到libz.1.2.5.dylib,选中并点击Add。

zip_03

现在,编译你的工程,应该会成功且没有任何错误。有一点需要注意的是ZipArchive可能会产生一些警告,这没什么大不了,但是如果你有代码洁癖(你应该有),深挖代码,看你自己能否解决他们。

下载和解压文件

下边我会展示从web下载、解压和在项目中使用zip文件是如何的简单。这里使用的下载代码非常基础,如果没有更多的调试可能无法用在实际产品中。

例子工程拥有一个类似这样的view:

zip_04

这就是一个UIImageView和UILabel,在view的controller里我为他们添加了IBOutlet,下载例子工程可以查看具体的实现。我们会从web下载一个包含了图片和文本的zip文件,你可以从上边的截图看到他们。解压之后,图片会被设置到UIImageView,文本里的内容会显示到UILabel。

1. 应用ZipArchive头文件

#import "ZipArchive.h"

2. 下载zip文件

// 1 
dispatch_queue_t queue = dispatch_get_global_queue(
                                                   DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
    NSURL *url = [NSURL URLWithString:@"http://www.icodeblog.com/wp-content/uploads/2012/08/zipfile.zip"];
    NSError *error = nil;
    // 2
    NSData *data = [NSData dataWithContentsOfURL:url options:0 error:&error];
    if(!error)
    {        
        // 3
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
        NSString *path = [paths objectAtIndex:0];
        NSString *zipPath = [path stringByAppendingPathComponent:@"zipfile.zip"];
        [data writeToFile:zipPath options:0 error:&error];
        if(!error)
        {
            // TODO: Unzip
        }
        else
        {
            NSLog(@"Error saving file %@",error);
        }
    }
    else
    {
        NSLog(@"Error downloading zip file: %@", error);
    }
});

上边的代码会从iCodeBlog下载zip文件,并保存到应用的caches目录。步骤是:

1)创建队列以默认的优先级运行我们的代码
2)从web获取数据
3)解析出caches目录路径,并把下载到的数据写入到本地

现在你已经把文件下载到了本地磁盘,是时候解压并使用这些文件了。

3、解压下载的文件

最后一步是解压你刚刚下载到的文件。有一点要明确的是,zip存储在/Library/Caches/zipfile.zip,解压出来的内容也在这个目录。

用下边的代码代替上边代码里的// TODO: Unzip

ZipArchive *za = [[ZipArchive alloc] init];
// 1
if ([za UnzipOpenFile: zipPath]) {      
    // 2      
    BOOL ret = [za UnzipFileTo: path overWrite: YES];
    if (NO == ret){} [za UnzipCloseFile];
    // 3
    NSString *imageFilePath = [path stringByAppendingPathComponent:@"photo.png"];
    NSString *textFilePath = [path stringByAppendingPathComponent:@"text.txt"];
    NSData *imageData = [NSData dataWithContentsOfFile:imageFilePath options:0 error:nil];
    UIImage *img = [UIImage imageWithData:imageData];
    NSString *textString = [NSString stringWithContentsOfFile:textFilePath 
        encoding:NSASCIIStringEncoding error:nil];
    // 4           
    dispatch_async(dispatch_get_main_queue(), ^{
        self.imageView.image = img;
        self.label.text = textString;
    });

下边解释这段代码是如何工作的:

1)打开文件,并在内存中解压
2)把解压出的内存写入caches目录
3)使用解压出的文件
4)用刚获取到的数据更新UI

真的就这么简单。

压缩文件

下边你会看到如何压缩文件。用户需要通过web或者email分享多个文件的时候,这显得非常方便。如果你完成了上边的步骤,caches目录会有些文件可供我们重新压缩并发送。我会压缩你刚刚解压的两个文件,把他们填充到一个新的zip文件中,并把新的zip文件写入documents目录。

在我的例子工程中,我创建了一个叫做“Zip Files”的按钮,它按下时会链接到一个名为zipFileButtonPressed:的IBAction,这里我们会进行压缩工作:

- (IBAction)zipFilesButtonPressed:(id)sender
{
    // 1
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docspath = [paths objectAtIndex:0];
    // 2
    paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *cachePath = [paths objectAtIndex:0];
    // 3
    NSString *zipFile = [docspath stringByAppendingPathComponent:@"newzipfile.zip"];       
    // 4
    ZipArchive *za = [[ZipArchive alloc] init];
    [za CreateZipFile2:zipFile];
    // 5
    NSString *imagePath = [cachePath stringByAppendingPathComponent:@"photo.png"];
    NSString *textPath = [cachePath stringByAppendingPathComponent:@"text.txt"];
    // 6
    [za addFileToZip:imagePath newname:@"NewPhotoName.png"];
    [za addFileToZip:textPath newname:@"NewTextName.txt"];
    // 7
    BOOL success = [za CloseZipFile2];    
    NSLog(@"Zipped file with result %d",success);
}

这里是上边代码的工作原理:

1)解析出documents目录的路径,在需要写新的zip文件时需要这个路径。
2)解析出caches目录的路径,我们需要通过它获取需要被压缩的文件。
3)获取新zip文件的路径。
4)创建ZipArchive类的实例,并告诉它在内存里创建一个新的zip文件。注意:在调用CloseZipFile2方法之前,这个新的zip文件不会写到磁盘上
5)获取将要被压缩文件的路径
6)把要压缩的文件添加到ZipArchive的实例,你可以在这里添加多个文件,甚至可以添加目录(如果你愿意,你可以添加整个caches目录)。
7)把zip文件写到磁盘,我输出了结果以便确认压缩是否成功。

运行这个应用,点击“Zip Filse”按钮,查看应用的documents目录,在~/Library/Application Support/iPhone Simulator/[iOS Version]/Applications/[Unique ID]/Documents。这个目录应该包含一个名为newzipfile.zip的压缩文件,如果你解压,你可以看到你压缩的文件。

结束语

你现在知道如何使用ZipArchive库在iOS设备上压缩和解压文件。你可以在下边下载例子工程。如果你有任何问题和建议,请在这篇文章后留言或者在Twitter上联系我@brandontreb(原作者)。

下载例子工程