iOS下载H5游戏zip包,并解压在本地运行H5游戏

前言:

  • 这篇没有太多技术细节以及底层知识,仅仅是解决需求的操作步骤以及解决方案。
  • 代码会放在Github上,希望大家一起讨论下存在的问题,以及更好的解决方案。
  • 正文的代码大部分是伪代码,供学习以及讨论用,因此没有写太多防御式编程的思想,如果用于工作中,需要自己做好防御式编程的措施。

为防止有着同样需求的伙伴无法通过关键字搜索到本文,所以将标题起的如此冗长以及不符合正常标题的起名规范,希望大家谅解,这篇文目的不是为了分享知识,而是为了解决需求以及问题,这个下载器是我们做H5游戏实时匹配对战时用到一个小组件,简要的说下实现原理。

需求如下

  • 从后端API接口下载游戏压缩包,并保存在本地。
  • 在本地沙盒目录进行解压缩
  • 解压成功后保存在本地指定目录,进行文件管理
  • 如果版本号变更,需要将本地游戏删除,并保存最新版本的H5游戏到本地
  • 在本地搭建代理服务器,通过iOS本地服务器以及端口号,运行H5游戏。

我的思路

尽量用最简洁的方法来实现功能,尽量用最新的API来解决技术场景,为了供大家理解流程。简化不需要的冗余代码。

一、关于API下载

下载流程图

下载流程类方法流程图.png
中文版
下载流程类方法流程图(中文版).png

建立游戏模块Model

主要用到的字段有:H5下载链接,游戏唯一标识符,游戏版本号字段等。

/**
 * 游戏字段 Model
 */
@interface WPGGamePageGames : NSObject
// 游戏id
@property (nonatomic, copy) NSString *gameId;
// H5游戏版本
@property (nonatomic, copy) NSString *h5Version;
// H5游戏包下载
@property (nonatomic, copy) NSString *h5Down;
// H5游戏包MD5
@property (nonatomic, copy) NSString *h5Md5;

@end

调用 NSURLSession API进行请求下载

对NSURLSession进行初始化加载:

- (NSURLSession *)session
{
    if (!_session) {
        _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
                                                 delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}

建立一个下载请求,如果不需要断点续传,可以自动忽略 (void)pause 和 (void)resume 方法。


@property (nonatomic, strong) NSURLSessionDownloadTask *task;
@property (nonatomic, strong) NSData *resumeData;

// 开始创建下载请求并进行下载(_gameModel.h5Down 为H5游戏的下载链接)
- (void)start
{
    NSURL *url = [NSURL URLWithString:_gameModel.h5Down];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"GET";
    self.task = [self.session downloadTaskWithRequest:request];
    [self.task resume];
}

// 暂停下载请求
- (void)pause
{
    __weak __typeof(self) weakSelf = self;
    [self.task cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
        weakSelf.resumeData = resumeData;
        weakSelf.task = nil;
    }];
}

// 恢复下载请求进行断点续传
- (void)resume
{
    self.task = [self.session downloadTaskWithResumeData:self.resumeData];
    [self.task resume];
    self.resumeData = nil;
}

4个重要下载回调代理如下:

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    // 展示下载进度
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    // 下载成功
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    if (error) {
        // 下载失败
    }
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
    // 断点续传的代理
}

二、在本地沙盒目录进行解压缩

解压缩需要用到第三方库,SSZipArchive下载地址如下:https://github.com/ZipArchive/ZipArchive

注意 SSZipArchive 引入到工程时需要添加 libz.tbd 库,否则编译时通不过。使用SSZipArchive解压文件的方法为:

    BOOL ret1 = [SSZipArchive unzipFileAtPath:file toDestination:destination delegate:self];
    if (!ret1) {
        NSLog(@"解压失败");
        return;
    }

SSZipArchive解压缩的代理方法如下


- (void)zipArchiveWillUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo
{
    NSLog(@"将要解压。"); 
}

- (void)zipArchiveDidUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo unzippedPath:(NSString *)unzippedPath
{
    NSLog(@"解压完成!");  
}

关于SSZipArchive所有方法说明详细API以及调用方法,已放在附录栏中,不在正文说明。

三、文件管理

当游戏较多的时候,我们需要进行本地管理,我的管理目录如下:

Library/Caches/Game/(文件夹名为:GameId)/H5游戏资源存放的地方

由于GameId为所有H5游戏的唯一标识符,所以我的做法是将所有H5游戏存放在Caches/Game/文件目录下,文件路径命名为GameId,这便不会出现游戏重名导致游戏覆盖安装的情况。

当版本更新的时候,之前存在沙盒目录中的游戏需要删除,并重新下载安装,我通过本地数据库去存取版本信息。当然首先要装一个本地数据库,我是通过FMDB第三方库进行管理。

数据表字段如下:Id(自增Id),gameId(游戏唯一标识符),version(游戏版本号),exist(文件是否在本地存在)

版本管理业务逻辑:

1)若数据库表中GameId存在,版本号在数据库列表中不存在,则删除本地GameId的文件夹进行下载
2)若数据库表中GameId不存在,则直接进行下载H5游戏保存在本地
3)若数据库表中GameId存在且版本号也存在,则直接读取Library/Caches/Game/(文件夹名为:GameId)目录,加载H5游戏资源。
4)当游戏下载成功后解压到Library/Caches/Game/(文件夹名为:GameId)目录下,将数据库写入最新的GameId,version字段写入数据表中。表明本地存在Gameid=XX,version=X.X.X的H5游戏。

本地数据库操作代码如下:

#import "FMDB.h"

static WPGameDownloader *_instance;

@interface WPGameDownloader() <NSURLSessionDownloadDelegate, SSZipArchiveDelegate>
{
    FMDatabase *_db;
}

+ (instancetype)shareManager
{
    if (!_instance) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _instance = [[self alloc] init];
        });
    }
    return _instance;
}

- (id)init
{
    if (self = [super init]) {
        _queenArray = [NSMutableArray new];
        _isDownloading = NO;
        [self createTable];
    }
    return self;
}

// 创建表
- (void)createTable
{
    //先打开数据库,然后创建表,最后关闭数据库
    if (![self openDataBase]) {
        return;
    }
    //tableExists 判断表是否存在,当表不存在的时候再去创建  参数:表名
    if (![_db tableExists:@"Game_Version_isExist"]) {
        NSString *sql = @"CREATE TABLE IF NOT EXISTS Game_Version_IsExist ('ID' INTEGER PRIMARY KEY AUTOINCREMENT,'gameid' TEXT NOT NULL, 'version' TEXT NOT NULL,'exist' INTEGER NOT NULL)";
        BOOL result = [_db executeUpdate:sql];
        if (result) {
            NSLog(@"create table success");
        }
    }
    [_db close];
}

// 打印全部数据
- (void)printAllData
{
    if (![self openDataBase]) {
        return;
    }
    
    FMResultSet *set = [_db executeQuery:@"SELECT * FROM Game_Version_isExist"];
    // next 单步查询
    while ([set next]) {组
        NSLOG(@"===%d,%@,%@,%d", [set intForColumnIndex:0],[set stringForColumn:@"gameid"],[set stringForColumn:@"version"],[set intForColumn:@"exist"]);
    }
    [set close];
    [_db close];
}

// 查询全部数据
- (NSMutableArray *)selectAllData
{
    if (![self openDataBase]) {
        return nil;
    }
    
    FMResultSet *set = [_db executeQuery:@"SELECT * FROM Game_Version_isExist"];
    NSMutableArray *array = [NSMutableArray array];
    // next 单步查询
    while ([set next]) {
        //把每一条数据(包含id,name,phone),存入一个对象,再把对象放入数组
        WPGameDataBaseModel *game = [[WPGameDataBaseModel alloc] init];
        game.auto_id = [set intForColumnIndex:0];
        game.gameId = [set stringForColumn:@"gameid"];
        game.version = [set stringForColumn:@"version"];
        game.value = [set intForColumn:@"exist"];
        //把查询的每一条数据分别放入数组
        [array addObject:game];
    }
    [set close];
    [_db close];
    return array;
}

- (BOOL)searchDataGameId:(NSString *)gameId versiton:(NSString *)version
{
    [self printAllData];
    if (![self openDataBase]) {
        return NO;
    }
    NSString *sql = [NSString stringWithFormat:@"SELECT * FROM Game_Version_isExist WHERE gameid = %@ and version = '%@'", gameId,version];
    FMResultSet *set = [_db executeQuery:sql];
    BOOL returnValue = NO;
    while ([set next]) {
        returnValue = YES;
    }
    [set close];
    [_db close];
    return returnValue;
}

// 增加数据
- (void)inserIntoData:(WPGameDataBaseModel *)gameModel
{
    if ([self openDataBase]) {
        [_db executeUpdateWithFormat:@"DELETE FROM Game_Version_IsExist WHERE gameid = %@", gameModel.gameId];
        [_db executeUpdateWithFormat:@"INSERT INTO Game_Version_IsExist (gameid, version, exist) VALUES (%@,%@,%d)",gameModel.gameId, gameModel.version,gameModel.value];
        [_db close];
    }
}

// 通过gameId进行修改
- (void)updateData:(WPGameDataBaseModel *)gameModel
{
    //根据id找到具体的联系人
    if ([self openDataBase]) {
        [_db executeUpdateWithFormat:@"UPDATE Game_Version_IsExist SET version = %@, exist = %d WHERE gameid = %@",gameModel.version,gameModel.value,gameModel.gameId];
        [_db close];
    }
}

// 删除
- (void)deleteData:(NSString *)game_id
{
    if ([self openDataBase]) {
        //根据联系人的id进行删除
        [_db executeUpdateWithFormat:@"DELETE FROM Game_Version_IsExist WHERE gameid = %@",game_id];
        [_db close];
    }
}

三、通过代理服务器运行H5游戏

JS游戏想要运行到iOS设备的WebView, 是需要自己搭建一套Web Server的。通过CocoaHTTPServer三方库的这个可以满足我们的需求,CocoaHTTPServer是个很强大的三方库,不但可以通过加载H5游戏运行在本地,还能通过ip局域网和电脑传输文件,作用强大。

项目中, 我使用了 Cocoapods 来管理第三方库.在 podfile 中直接添加下面的代码:

pod 'CocoaHTTPServer', '~> 2.3'

然后 pod install 即可

主要的核心代码如下:

@property (nonatomic, strong) HTTPServer *localHttpServer;

- (void)_configLocalHttpServer
{
    NSString *webPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches/Game/snake/game/"];
    _localHttpServer = [[HTTPServer alloc] init];
    [_localHttpServer setType:@"_http.tcp"];
    
    NSFileManager *fileManager = [[NSFileManager alloc] init];
    NSLog(@"%@", webPath);
    
    if (![fileManager fileExistsAtPath:webPath]) {
        NSLog(@"File path error!");
    }
    else {
        NSString *webLocalPath = webPath;
        [_localHttpServer setDocumentRoot:webLocalPath];
        NSLog(@"webLocalPath:%@", webLocalPath);
        [self _startWebServer];
    }
}

- (void)_startWebServer
{
    NSError *error;
    if ([_localHttpServer start:&error]) {
        NSLog(@"Started HTTP Server on port %hu", [_localHttpServer listeningPort]);
        NSLog(@"Start Server Successfully.");
        self.port = [NSString stringWithFormat:@"%d", [_localHttpServer listeningPort]];
        _startServerSuccess = YES;
        
        UIWebView *webView = [[UIWebView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
        [self.view addSubview:webView];
        
        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%@/index.html", self.port]];
        [webView loadRequest:[NSURLRequest requestWithURL:url]];
        
    }
    else {
        NSLog(@"Error starting HTTP Server: %@", error);
        _startServerSuccess = NO;
    }
}

到这里流程就已经跑通了,然后说一下我下载器的实现。

我的实现

代理以及方法

.h文件所提供的方法如下:

  1. 通过 isExistGame:gameModel 方法来判断本地是否存在这个H5游戏。
  2. 通过 downloadGameModel:gameModel 方法来下载游戏,下载多个游戏的时候,是通过串行队列的方式进行下载。
  3. 下载进度在delegate中进行监听,下载成功或者失败也在delegate中进行监听
@protocol WPGameDownloaderDelegate <NSObject>

// 下载成功回调(通过gameId判断是否为本次下载任务)
- (void)downloadSuccess:(WPGameDownloader *)gamedownloader gameId:(NSString *)gameId;
// 下载失败回调(通过gameId判断是否为本次下载任务)
- (void)downloadFail:(WPGameDownloader *)gamedownloader gameId:(NSString *)gameId;
// 下载进度回调(通过gameId判断是否为本次下载任务)
- (void)downloadProgress:(NSInteger)progress gamedownloader:(WPGameDownloader *)gamedownloader gameId:(NSString *)gameId;

@end

@interface WPGameDownloader : NSObject

@property (nonatomic, weak) id <WPGameDownloaderDelegate> delegate;

// 单例
+ (instancetype)shareManager;

// 下载游戏
- (void)downloadGameModel:(WPGGamePageGames *)gameModel;

// 游戏是否下载到本地
- (BOOL)isExistGame:(WPGGamePageGames *)gameModel;

@end

声明变量并用单例进行实现

通过单例对H5游戏下载器进行更安全的控制,以防止并行下载会出现的问题,以及多线程同时读写数据库出现的问题。

@interface WPGameDownloader() <NSURLSessionDownloadDelegate, SSZipArchiveDelegate>
{
    FMDatabase *_db;
}

@property (nonatomic, strong) NSURLSession *session;
@property (nonatomic, strong) NSURLSessionDownloadTask *task;
@property (nonatomic, strong) NSMutableArray *queenArray;
@property (nonatomic, strong) WPGGamePageGames *gameModel;
@property (nonatomic, assign) BOOL isDownloading;

@end

@implementation WPGameDownloader

+ (instancetype)shareManager
{
    if (!_instance) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _instance = [[self alloc] init];
        });
    }
    return _instance;
}

- (id)init
{
    if (self = [super init]) {
        _queenArray = [NSMutableArray new];
        _isDownloading = NO;
        [self createTable];
    }
    return self;
}

下载器已经将代码放入github中,关于下载器的部分下载操作如下:

- (BOOL)isExistGame:(WPGGamePageGames *)gameModel
{
    return [self searchDataGameId:gameModel.gameId versiton:gameModel.h5Version];
}

- (void)downloadGameModel:(WPGGamePageGames *)gameModel
{
    [_queenArray addObject:gameModel];
    [self startDownLoad];
}

- (void)startDownLoad
{
    if (_queenArray.count<=0) {
        return;
    }
    if (_isDownloading) {
        return;
    }
    _isDownloading = YES;
    _gameModel = [_queenArray cl_objectAtIndex:0];
    [_queenArray cl_removeObjectAtIndex:0];
    [self start];
}

- (void)removefile
{
    NSFileManager *manager = [NSFileManager defaultManager];
    NSString *docsDir = [NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"Library/Caches/Game/%@/", _gameModel.gameId]];
    [manager removeItemAtPath:docsDir error:nil];
}

- (void)downloadFailed
{
    _isDownloading = NO;
    IDSLOG(@"FAILED : GAMEID: %@", _gameModel.gameId);
    [self removefile];
    [self.session finishTasksAndInvalidate];
    _session = nil;
    if ([self.delegate respondsToSelector:@selector(downloadFail:gameId:)]) {
        [self.delegate downloadFail:self gameId:_gameModel.gameId];
    }
    [self startDownLoad];
}

- (void)downloadSuccess
{
    IDSLOG(@"SUEECSS : GAMEID: %@", _gameModel.gameId);
    WPGameDataBaseModel *model = [[WPGameDataBaseModel alloc] init];
    model.gameId = _gameModel.gameId;
    model.version = _gameModel.h5Version;
    model.value = 1;
    [self inserIntoData:model];
    if ([self.delegate respondsToSelector:@selector(downloadSuccess:gameId:)]) {
        [self.delegate downloadSuccess:self gameId:model.gameId];
    }
    _isDownloading = NO;
    [self startDownLoad];
}

- (void)start
{
    NSURL *url = [NSURL URLWithString:_gameModel.h5Down];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"GET";
    self.task = [self.session downloadTaskWithRequest:request];
    [self.task resume];
}

# pragma mark - delegate

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    if ([self.delegate respondsToSelector:@selector(downloadProgress:gamedownloader:gameId:)]) {
        [self.delegate downloadProgress:(totalBytesWritten*100/totalBytesExpectedToWrite) gamedownloader:self gameId:_gameModel.gameId];
    }
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location;
{
    [self createGameFolder];
    
    [self createGameFolderName:_gameModel.gameId];

    NSString *docPath = [NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"Library/Caches/Game/%@/", _gameModel.gameId]];
    NSString *file = [docPath stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
    NSError *error = nil;
    NSFileManager *manager = [NSFileManager defaultManager];
    
    BOOL result = [manager fileExistsAtPath:location.path];
    NSLog(@"移动之前 这个文件已经存在:%@",result?@"是的":@"不存在");
    if ([manager fileExistsAtPath:location.path]) {
        NSLog(@"移动之前文件大小为: %.1fM", [[manager attributesOfItemAtPath:location.path error:nil] fileSize]/1000000.0);
    }
    if (![[manager attributesOfItemAtPath:location.path error:nil] fileSize]) {
        NSLog(@"文件为空返回");
        return;
    }
    // 判断文件是否存在
    BOOL ret = [manager moveItemAtPath:location.path toPath:file error:&error];
    if (!ret) {
        NSLog(@"MOVE FILE IS WRONG");
    }
    if (error) {
        NSLog(@"move failed:%@", [error localizedDescription]);
    }
    
    BOOL resultdd = [manager fileExistsAtPath:file];
    NSLog(@"移动之后 这个文件已经存在:%@",resultdd?@"是的":@"不存在");
    NSLog(@"储存路径 移动之后:%@, \n移动之前:%@",file,location.path);
    
    NSString *destination = [NSString stringWithFormat:@"%@/", docPath];
    BOOL ret1 = [SSZipArchive unzipFileAtPath:file toDestination:destination delegate:self];
    if (!ret1) {
        NSLog(@"解压失败");
        [self downloadFailed];
        return;
    }
    [manager removeItemAtPath:file error:nil];
    // 遍历文件
    NSDirectoryEnumerator *dirEnum = [manager enumeratorAtPath:docPath];
    NSString *fileName;
    while (fileName = [dirEnum nextObject]) {
        NSLog(@"FileFull>>> : %@" , [docPath stringByAppendingPathComponent:fileName]) ;
    }
    
    [self downloadSuccess];
}

- (long long)fileSizeAtPath:(NSString *)filePath {
    NSFileManager *manager = [NSFileManager defaultManager];
    if ([manager fileExistsAtPath:filePath]) {
        return [[manager attributesOfItemAtPath:filePath error:nil] fileSize];
    }
    return 0;
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    if (error) {
        [self downloadFailed];
    }
}

写在最后

上面的例子本人亲自实践过的, 给大家提供了一个实现思路, 算是抛砖引玉.

如果想做好这个模式, 还需要很多工作要做, 这里列出来给大家分享一下.

1.游戏资源包管理和下载.
2.游戏中需要和 Native 的交互逻辑.
3.数据加密.
4.移动端游戏本身的加载优化.

代码示例我放在了 GitHub上。
下载器代码如下:https://github.com/Yulei-Duan/WPGameDownloader
游戏代理服务器代码如下:https://github.com/Yulei-Duan/testProjestAboutGameRun

有问题,请在下面评论, 非常感谢能来看我的Demo分享!

附录

SSZipArchive所有方法说明

@interface SSZipArchive : NSObject  
  
// Unzip 解压  
/** 
 * @param          path    源文件 
 * @param   destination    目的文件 
 * @param      uniqueId    标记,用于区别多个解压操作 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示解压失败。 
 */  
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination  uniqueId:(NSString *)uniqueId;  
  
/** 
 * @param          path    源文件 
 * @param   destination    目的文件 
 * @param     overwrite    YES 会覆盖 destination 路径下的同名文件,NO 则不会。 
 * @param      password    需要输入密码的才能解压的压缩包 
 * @param         error    返回解压时遇到的错误信息 
 * @param      uniqueId    标记,用于区别多个解压操作 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示解压失败。 
 */  
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(NSString *)password error:(NSError **)error  uniqueId:(NSString *)uniqueId;  
  
/** 
 * @param          path    源文件 
 * @param   destination    目的文件 
 * @param      delegate    设置代理 
 * @param      uniqueId    标记,用于区别多个解压操作 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示解压失败。 
 */  
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination delegate:(id<SSZipArchiveDelegate>)delegate  uniqueId:(NSString *)uniqueId;  
  
/** 
 * @param          path    源文件 
 * @param   destination    目的文件 
 * @param     overwrite    YES 会覆盖 destination 路径下的同名文件,NO 则不会。 
 * @param      password    需要输入密码的才能解压的压缩包 
 * @param         error    返回解压时遇到的错误信息 
 * @param      delegate    设置代理 
 * @param      uniqueId    标记,用于区别多个解压操作 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示解压失败。 
 */  
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(NSString *)password error:(NSError **)error delegate:(id<SSZipArchiveDelegate>)delegate uniqueId:(NSString *)uniqueId;  
  
// Zip 压缩  
/** 
 * @param       path    目的路径(格式:~/xxx.zip 结尾的路径) 
 * @param  filenames    要压缩的文件路径 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示压缩失败。 
 */  
+ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray *)filenames;  
  
/** 
 * @param       path    目的路径(格式:~/xxx.zip 结尾的路径) 
 * @param  filenames    要压缩的文件目录路径 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示压缩失败。 
 */  
+ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath;  
  
/** 
 * 初始化压缩对象 
 * 
 * @param  path    目的路径(格式:~/xxx.zip 结尾的路径) 
 * 
 * @return 初始化后的对像 
 */  
- (id)initWithPath:(NSString *)path;  
  
/** 
 *  打开压缩对象 
 * @return 返回 YES 表示成功,返回 NO 表示失败。 
 */  
- (BOOL)open;  
  
/** 
 * 添加要压缩的文件的路径 
 * 
 * @param  path    文件路径 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示失败。 
 */  
- (BOOL)writeFile:(NSString *)path;  
  
/** 
 * 向此路径的文件里写入数据 
 * 
 * @param      data    要写入的数据 
 * @param  filename    文件路径 
 * 
 * @return 返回 YES 表示成功,返回 NO 表示失败。 
 */  
- (BOOL)writeData:(NSData *)data filename:(NSString *)filename;  
  
/** 
 *  关闭压缩对象 
 * @return 返回 YES 表示成功,返回 NO 表示失败。 
 */  
- (BOOL)close;  
  
@end  
  
  
@protocol SSZipArchiveDelegate <NSObject>  
  
@optional  
  
//将要解压  
- (void)zipArchiveWillUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo;  
//解压完成  
- (void)zipArchiveDidUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo unzippedPath:(NSString *)unzippedPat uniqueId:(NSString *)uniqueId;  
//将要解压  
- (void)zipArchiveWillUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo;  
//解压完成  
- (void)zipArchiveDidUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo;  
  
@end

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容

  • 以前上学的时候,我参加了文学社社团,社长很喜欢我,经常将我写的文章登在校报上。 虽然社长一直鼓励我,但那时的我很不...
    米米慢阅读 446评论 7 2
  • 午夜知了还在不分昼的俞噪 一个人带着淡淡的醉意 身后 跟着又是谁 身后这 艰难拖着 很累很累 想找个地方 一弃了之...
    泠先森阅读 174评论 2 5