在这篇教程里,我将演示如何在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”。
注意:不支持ARC
如果你在项目中使用ARC,你需要告诉编译器不要为ZipArchive使用ARC。要配置这个请点击工程左边的列表,然后点击你的target并选择“Build Phases”标签。
展开“Compile Sources”,找到ZipArchive.mm并双击,在弹出的框中输入“-fno-objc-arc”并点击Done。
连接libz
最后一步是和libz.1.2.5.dylib连接,在Build Phases选项卡中展开Link Binary With Libraries节,并点击“+”按钮来添加新的库。从列表里找到libz.1.2.5.dylib,选中并点击Add。
现在,编译你的工程,应该会成功且没有任何错误。有一点需要注意的是ZipArchive可能会产生一些警告,这没什么大不了,但是如果你有代码洁癖(你应该有),深挖代码,看你自己能否解决他们。
下载和解压文件
下边我会展示从web下载、解压和在项目中使用zip文件是如何的简单。这里使用的下载代码非常基础,如果没有更多的调试可能无法用在实际产品中。
例子工程拥有一个类似这样的view:
这就是一个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(原作者)。