iOS下載H5游戲zip包芹助,并解壓在本地運行H5游戲

前言:

  • 這篇沒有太多技術(shù)細(xì)節(jié)以及底層知識堂湖,僅僅是解決需求的操作步驟以及解決方案。
  • 代碼會放在Github上状土,希望大家一起討論下存在的問題无蜂,以及更好的解決方案。
  • 正文的代碼大部分是偽代碼蒙谓,供學(xué)習(xí)以及討論用斥季,因此沒有寫太多防御式編程的思想,如果用于工作中累驮,需要自己做好防御式編程的措施酣倾。

為防止有著同樣需求的伙伴無法通過關(guān)鍵字搜索到本文,所以將標(biāo)題起的如此冗長以及不符合正常標(biāo)題的起名規(guī)范谤专,希望大家諒解躁锡,這篇文目的不是為了分享知識,而是為了解決需求以及問題毒租,這個下載器是我們做H5游戲?qū)崟r匹配對戰(zhàn)時用到一個小組件稚铣,簡要的說下實現(xiàn)原理箱叁。

需求如下

  • 從后端API接口下載游戲壓縮包,并保存在本地惕医。
  • 在本地沙盒目錄進(jìn)行解壓縮
  • 解壓成功后保存在本地指定目錄耕漱,進(jìn)行文件管理
  • 如果版本號變更,需要將本地游戲刪除抬伺,并保存最新版本的H5游戲到本地
  • 在本地搭建代理服務(wù)器螟够,通過iOS本地服務(wù)器以及端口號,運行H5游戲峡钓。

我的思路

盡量用最簡潔的方法來實現(xiàn)功能妓笙,盡量用最新的API來解決技術(shù)場景,為了供大家理解流程能岩。簡化不需要的冗余代碼寞宫。

一、關(guān)于API下載

下載流程圖

下載流程類方法流程圖.png
中文版
下載流程類方法流程圖(中文版).png

建立游戲模塊Model

主要用到的字段有:H5下載鏈接拉鹃,游戲唯一標(biāo)識符辈赋,游戲版本號字段等。

/**
 * 游戲字段 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

調(diào)用 NSURLSession API進(jìn)行請求下載

對NSURLSession進(jìn)行初始化加載:

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

建立一個下載請求膏燕,如果不需要斷點續(xù)傳钥屈,可以自動忽略 (void)pause 和 (void)resume 方法。


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

// 開始創(chuàng)建下載請求并進(jìn)行下載(_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;
    }];
}

// 恢復(fù)下載請求進(jìn)行斷點續(xù)傳
- (void)resume
{
    self.task = [self.session downloadTaskWithResumeData:self.resumeData];
    [self.task resume];
    self.resumeData = nil;
}

4個重要下載回調(diào)代理如下:

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    // 展示下載進(jìn)度
}

- (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
{
    // 斷點續(xù)傳的代理
}

二坝辫、在本地沙盒目錄進(jìn)行解壓縮

解壓縮需要用到第三方庫篷就,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(@"解壓完成!");  
}

關(guān)于SSZipArchive所有方法說明詳細(xì)API以及調(diào)用方法银锻,已放在附錄欄中永品,不在正文說明昌腰。

三悦施、文件管理

當(dāng)游戲較多的時候,我們需要進(jìn)行本地管理船殉,我的管理目錄如下:

Library/Caches/Game/(文件夾名為:GameId)/H5游戲資源存放的地方

由于GameId為所有H5游戲的唯一標(biāo)識符更振,所以我的做法是將所有H5游戲存放在Caches/Game/文件目錄下炕桨,文件路徑命名為GameId,這便不會出現(xiàn)游戲重名導(dǎo)致游戲覆蓋安裝的情況。

當(dāng)版本更新的時候肯腕,之前存在沙盒目錄中的游戲需要刪除献宫,并重新下載安裝,我通過本地數(shù)據(jù)庫去存取版本信息实撒。當(dāng)然首先要裝一個本地數(shù)據(jù)庫姊途,我是通過FMDB第三方庫進(jìn)行管理涉瘾。

數(shù)據(jù)表字段如下:Id(自增Id),gameId(游戲唯一標(biāo)識符),version(游戲版本號),exist(文件是否在本地存在)

版本管理業(yè)務(wù)邏輯:

1)若數(shù)據(jù)庫表中GameId存在,版本號在數(shù)據(jù)庫列表中不存在捷兰,則刪除本地GameId的文件夾進(jìn)行下載
2)若數(shù)據(jù)庫表中GameId不存在立叛,則直接進(jìn)行下載H5游戲保存在本地
3)若數(shù)據(jù)庫表中GameId存在且版本號也存在,則直接讀取Library/Caches/Game/(文件夾名為:GameId)目錄贡茅,加載H5游戲資源秘蛇。
4)當(dāng)游戲下載成功后解壓到Library/Caches/Game/(文件夾名為:GameId)目錄下,將數(shù)據(jù)庫寫入最新的GameId,version字段寫入數(shù)據(jù)表中顶考。表明本地存在Gameid=XX赁还,version=X.X.X的H5游戲。

本地數(shù)據(jù)庫操作代碼如下:

#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;
}

// 創(chuàng)建表
- (void)createTable
{
    //先打開數(shù)據(jù)庫,然后創(chuàng)建表,最后關(guān)閉數(shù)據(jù)庫
    if (![self openDataBase]) {
        return;
    }
    //tableExists 判斷表是否存在,當(dāng)表不存在的時候再去創(chuàng)建  參數(shù):表名
    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];
}

// 打印全部數(shù)據(jù)
- (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];
}

// 查詢?nèi)繑?shù)據(jù)
- (NSMutableArray *)selectAllData
{
    if (![self openDataBase]) {
        return nil;
    }
    
    FMResultSet *set = [_db executeQuery:@"SELECT * FROM Game_Version_isExist"];
    NSMutableArray *array = [NSMutableArray array];
    // next 單步查詢
    while ([set next]) {
        //把每一條數(shù)據(jù)(包含id,name,phone),存入一個對象驹沿,再把對象放入數(shù)組
        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"];
        //把查詢的每一條數(shù)據(jù)分別放入數(shù)組
        [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;
}

// 增加數(shù)據(jù)
- (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進(jìn)行修改
- (void)updateData:(WPGameDataBaseModel *)gameModel
{
    //根據(jù)id找到具體的聯(lián)系人
    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]) {
        //根據(jù)聯(lián)系人的id進(jìn)行刪除
        [_db executeUpdateWithFormat:@"DELETE FROM Game_Version_IsExist WHERE gameid = %@",game_id];
        [_db close];
    }
}

三艘策、通過代理服務(wù)器運行H5游戲

JS游戲想要運行到iOS設(shè)備的WebView, 是需要自己搭建一套Web Server的。通過CocoaHTTPServer三方庫的這個可以滿足我們的需求甚负,CocoaHTTPServer是個很強大的三方庫柬焕,不但可以通過加載H5游戲運行在本地,還能通過ip局域網(wǎng)和電腦傳輸文件梭域,作用強大。

項目中, 我使用了 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;
    }
}

到這里流程就已經(jīng)跑通了搅轿,然后說一下我下載器的實現(xiàn)病涨。

我的實現(xiàn)

代理以及方法

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

  1. 通過 isExistGame:gameModel 方法來判斷本地是否存在這個H5游戲。
  2. 通過 downloadGameModel:gameModel 方法來下載游戲璧坟,下載多個游戲的時候既穆,是通過串行隊列的方式進(jìn)行下載。
  3. 下載進(jìn)度在delegate中進(jìn)行監(jiān)聽雀鹃,下載成功或者失敗也在delegate中進(jìn)行監(jiān)聽
@protocol WPGameDownloaderDelegate <NSObject>

// 下載成功回調(diào)(通過gameId判斷是否為本次下載任務(wù))
- (void)downloadSuccess:(WPGameDownloader *)gamedownloader gameId:(NSString *)gameId;
// 下載失敗回調(diào)(通過gameId判斷是否為本次下載任務(wù))
- (void)downloadFail:(WPGameDownloader *)gamedownloader gameId:(NSString *)gameId;
// 下載進(jìn)度回調(diào)(通過gameId判斷是否為本次下載任務(wù))
- (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

聲明變量并用單例進(jìn)行實現(xiàn)

通過單例對H5游戲下載器進(jìn)行更安全的控制幻工,以防止并行下載會出現(xiàn)的問題,以及多線程同時讀寫數(shù)據(jù)庫出現(xiàn)的問題黎茎。

@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;
}

下載器已經(jīng)將代碼放入github中囊颅,關(guān)于下載器的部分下載操作如下:

- (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(@"移動之前 這個文件已經(jīng)存在:%@",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(@"移動之后 這個文件已經(jīng)存在:%@",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];
    }
}

寫在最后

上面的例子本人親自實踐過的, 給大家提供了一個實現(xiàn)思路, 算是拋磚引玉.

如果想做好這個模式, 還需要很多工作要做, 這里列出來給大家分享一下.

1.游戲資源包管理和下載.
2.游戲中需要和 Native 的交互邏輯.
3.數(shù)據(jù)加密.
4.移動端游戲本身的加載優(yōu)化.

代碼示例我放在了 GitHub上。
下載器代碼如下:https://github.com/Yulei-Duan/WPGameDownloader
游戲代理服務(wù)器代碼如下:https://github.com/Yulei-Duan/testProjestAboutGameRun

有問題傅瞻,請在下面評論, 非常感謝能來看我的Demo分享!

附錄

SSZipArchive所有方法說明

@interface SSZipArchive : NSObject  
  
// Unzip 解壓  
/** 
 * @param          path    源文件 
 * @param   destination    目的文件 
 * @param      uniqueId    標(biāo)記踢代,用于區(qū)別多個解壓操作 
 * 
 * @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    標(biāo)記溺森,用于區(qū)別多個解壓操作 
 * 
 * @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    設(shè)置代理 
 * @param      uniqueId    標(biāo)記,用于區(qū)別多個解壓操作 
 * 
 * @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    設(shè)置代理 
 * @param      uniqueId    標(biāo)記更胖,用于區(qū)別多個解壓操作 
 * 
 * @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 結(jié)尾的路徑) 
 * @param  filenames    要壓縮的文件路徑 
 * 
 * @return 返回 YES 表示成功却妨,返回 NO 表示壓縮失敗。 
 */  
+ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray *)filenames;  
  
/** 
 * @param       path    目的路徑(格式:~/xxx.zip 結(jié)尾的路徑) 
 * @param  filenames    要壓縮的文件目錄路徑 
 * 
 * @return 返回 YES 表示成功括眠,返回 NO 表示壓縮失敗彪标。 
 */  
+ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath;  
  
/** 
 * 初始化壓縮對象 
 * 
 * @param  path    目的路徑(格式:~/xxx.zip 結(jié)尾的路徑) 
 * 
 * @return 初始化后的對像 
 */  
- (id)initWithPath:(NSString *)path;  
  
/** 
 *  打開壓縮對象 
 * @return 返回 YES 表示成功,返回 NO 表示失敗掷豺。 
 */  
- (BOOL)open;  
  
/** 
 * 添加要壓縮的文件的路徑 
 * 
 * @param  path    文件路徑 
 * 
 * @return 返回 YES 表示成功捞烟,返回 NO 表示失敗。 
 */  
- (BOOL)writeFile:(NSString *)path;  
  
/** 
 * 向此路徑的文件里寫入數(shù)據(jù) 
 * 
 * @param      data    要寫入的數(shù)據(jù) 
 * @param  filename    文件路徑 
 * 
 * @return 返回 YES 表示成功当船,返回 NO 表示失敗题画。 
 */  
- (BOOL)writeData:(NSData *)data filename:(NSString *)filename;  
  
/** 
 *  關(guān)閉壓縮對象 
 * @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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末苍息,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子壹置,更是在濱河造成了極大的恐慌竞思,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钞护,死亡現(xiàn)場離奇詭異盖喷,居然都是意外死亡,警方通過查閱死者的電腦和手機难咕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門课梳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人步藕,你說我怎么就攤上這事惦界。” “怎么了咙冗?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵沾歪,是天一觀的道長。 經(jīng)常有香客問我雾消,道長灾搏,這世上最難降的妖魔是什么挫望? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮狂窑,結(jié)果婚禮上媳板,老公的妹妹穿的比我還像新娘。我一直安慰自己泉哈,他們只是感情好蛉幸,可當(dāng)我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著丛晦,像睡著了一般奕纫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上烫沙,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天匹层,我揣著相機與錄音,去河邊找鬼锌蓄。 笑死升筏,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瘸爽。 我是一名探鬼主播您访,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼剪决!你這毒婦竟也來了洋只?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤昼捍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后肢扯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妒茬,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年蔚晨,在試婚紗的時候發(fā)現(xiàn)自己被綠了乍钻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡铭腕,死狀恐怖银择,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情累舷,我是刑警寧澤浩考,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站被盈,受9級特大地震影響析孽,放射性物質(zhì)發(fā)生泄漏搭伤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一袜瞬、第九天 我趴在偏房一處隱蔽的房頂上張望怜俐。 院中可真熱鬧,春花似錦邓尤、人聲如沸拍鲤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽季稳。三九已至,卻和暖如春佩捞,著一層夾襖步出監(jiān)牢的瞬間绞幌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工一忱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留莲蜘,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓帘营,卻偏偏與公主長得像票渠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子芬迄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 以前上學(xué)的時候禀梳,我參加了文學(xué)社社團杜窄,社長很喜歡我,經(jīng)常將我寫的文章登在校報上算途。 雖然社長一直鼓勵我塞耕,但那時的我很不...
    米米慢閱讀 456評論 7 2
  • 午夜知了還在不分晝的俞噪 一個人帶著淡淡的醉意 身后 跟著又是誰 身后這 艱難拖著 很累很累 想找個地方 一棄了之...
    泠先森閱讀 175評論 2 5