weex更新方案探索(三)

来源:互联网 发布:windows下载单机游戏 编辑:程序博客网 时间:2024/06/05 02:09

created by zhenggl

weex更新方案的探索,总结归档成系列文章:


1. weex更新方案探索(一)【weex更新方案整体思路】
2. weex更新方案探索(二)【weex更新方案vue端实现】
3. weex更新方案探索(三)【weex更新方案IOS端实现】【本篇】
4. weex更新方案探索(四)【weex更新方案Android端实现】
5. weex更新方案探索(五)【weex更新方案服务器端实现】
6. weex更新方案探索(六)【创建工具构建版本配置文件】
7. weex更新方案探索(七)【遗留问题或后续工作】


weex更新方案探索(三) ——weex更新方案IOS端实现

前提

IOS工程配置信息,参考《第一个weex程序》中 配置IOS环境 的部分。
不同之处,本篇需要使用在线js文件,config.xml作如下修改:

<!--////////////////////////【重要】////////////////////////////////////-->    <!-- 本地 或 在线资源 开关: true使用本地,false使用在线 -->    <preference name="launch_locally" value="false" /><!--///////////////////////////////////////////////////////////////////--><!--////////////////////////【在线】////////////////////////////////////-->    <!-- launch_locally:false 使用在线的资源:launch_url不为空,使用launch_url的启动地址;launch_url为空,使用online_start_url:+port -->    <!-- 公网地址 -->    <preference name="launch_url" value="http://www.xxx.com/xxx/dist/xxx/login/auto_login.js"/>

目的

C端要实现公用缓存,下载、进度、解压,页面跳转,页面刷新,防篡改机制。
故在原项目WXEventModule中添加相应的方法提供B端调用。


B端如何与C端交互?

这任务就交给我吧——WXEventModule!!

添加以下方法,暴露给B端调用,需要注意的:涉及多次回调结果给B疯掉的,callback要定义为WXModuleKeepAliveCallback。

说明:
涉及到的:LoaderViewController,LandscapeLoaderViewController,是基于WXDemoViewController自定义的扩展横竖屏处理和url拼装等的ViewController,限于篇幅,本文不罗列。
涉及到的:Singleton.h,可自行谷歌或百度一下。
涉及到的:DownloadUtil,FileUtil,ZipUtil,本文会在后面篇幅贴出代码。

WX_EXPORT_METHOD(@selector(openURL:))- (void)openURL:(id)params {    [self jumpDeal:params callback:nil isLandscape:NO isJumpToRoot:NO];}WX_EXPORT_METHOD(@selector(openURL:callback:))- (void)openURL:(id)params callback:(WXModuleCallback)callback {    [self jumpDeal:params callback:callback isLandscape:NO isJumpToRoot:NO];}WX_EXPORT_METHOD(@selector(openURLtoLandscape:))- (void)openURLtoLandscape:(id)params {    [self jumpDeal:params callback:nil isLandscape:YES isJumpToRoot:NO];}WX_EXPORT_METHOD(@selector(openURLtoLandscape:callback:))- (void)openURLtoLandscape:(id)params callback:(WXModuleCallback)callback {    [self jumpDeal:params callback:callback isLandscape:YES isJumpToRoot:NO];}//设置根导航WX_EXPORT_METHOD(@selector(openURLToRoot:))- (void)openURLToRoot:(id)params {    NSLog(@"WXEventModule.m --- openURLToRoot 设置导航根控制器");    [self jumpDeal:params callback:nil isLandscape:NO isJumpToRoot:YES];}//设置根导航WX_EXPORT_METHOD(@selector(openURLToRoot:callback:))- (void)openURLToRoot:(id)params callback:(WXModuleCallback)callback {    NSLog(@"WXEventModule.m --- openURLToRoot 设置导航根控制器");    [self jumpDeal:params callback:callback isLandscape:NO isJumpToRoot:YES];}- (void)jumpDeal:(id)params callback:(WXModuleCallback)callback isLandscape:(BOOL)isLandscape isJumpToRoot:(BOOL)isJumpToRoot {    if ([params isKindOfClass:[NSDictionary class]]) {        [self jumpPageWithDic:(NSDictionary *)params callback:callback isLandscape:isLandscape isJumpToRoot:isJumpToRoot];    } else if ([params isKindOfClass:[NSString class]]) {        [self jumpPageWithUrl:(NSString *)params callback:callback isLandscape:isLandscape isJumpToRoot:isJumpToRoot];    } else {        [self dealCallback:callback isSuccess:NO log:@"参数为非字典,也非字符串,不处理"];    }}- (void)jumpPageWithDic:(NSDictionary *)dic callback:(WXModuleCallback)callback isLandscape:(BOOL)isLandscape isJumpToRoot:(BOOL)isJumpToRoot {    NSString *fileName = dic[@"fileName"];    NSString *url = dic[@"url"];    NSString *fileMD5 = dic[@"fileMD5"];    //获取真实的地址    NSString *newURL = [self getRealUrlPath:url];    //可以进行页面渲染    [self jumpPage:fileName url:newURL fileMD5:fileMD5 isLandscape:isLandscape callback:callback isJumpToRoot:isJumpToRoot];}- (void)jumpPageWithUrl:(NSString *)url callback:(WXModuleCallback)callback isLandscape:(BOOL)isLandscape isJumpToRoot:(BOOL)isJumpToRoot {    //获取真实的地址    NSString *newURL = [self getRealUrlPath:url];    //可以进行页面渲染    [self jumpPage:@"" url:newURL fileMD5:nil isLandscape:isLandscape callback:callback isJumpToRoot:isJumpToRoot];}- (void)dealCallback:(WXModuleCallback)callback isSuccess:(BOOL)isSuccess log:(NSString *)log {    NSLog(@"%@", log);    if (callback) {        callback(@{ @"isSuccess": isSuccess ? @"true" : @"false" });    }}- (void)jumpPage:(NSString *)fileName url:(NSString *)url fileMD5:(NSString *)fileMD5 isLandscape:(BOOL)isLandscape callback:(WXModuleCallback)callback isJumpToRoot:(BOOL)isJumpToRoot {    //判断文件是否可以跳转    BOOL isCanJump = [self checkPageIsCanJump:fileName url:url fileMD5:fileMD5];    //是否需要判断,过滤掉不是我们域名的url???    //    if (isCanJump) {        [self dealCallback:callback isSuccess:YES log:@"跳转处理"];        UIViewController *controller = nil;        if (!isLandscape) {            controller = [[LoaderViewController alloc] init];            ((LoaderViewController *)controller).fileName = fileName;            ((LoaderViewController *)controller).url = [NSURL URLWithString:url];        } else {            controller = [[LandscapeLoaderViewController alloc] init];            ((LandscapeLoaderViewController *)controller).fileName = fileName;            ((LandscapeLoaderViewController *)controller).url = [NSURL URLWithString:url];        }        if (!isJumpToRoot) {            //普通跳转            [[weexInstance.viewController navigationController] pushViewController:controller animated:YES];        } else {            //跳转到根页面            NSMutableArray *arr = [[NSMutableArray alloc]initWithArray:[[weexInstance.viewController navigationController] viewControllers]];            if (arr != nil && arr.count > 0) {                NSLog(@"WXEventModule.m --- finish 导航里页面数量1个及以上,可以移除所有页面,再添加当前页面进导航");                if ([WeexSDKManager isTest]) {                    NSInteger ac = [arr count];                    for (NSInteger i = ac-1; i >= 0 ; i--) {                        UIViewController *tmp = [arr objectAtIndex:i];                        if ([tmp isKindOfClass:[StartViewController class]]) {                            //测试模式,不去除测试页面                            continue;                        } else {                            [arr removeObject:tmp];                        }                    }                } else {                    [arr removeAllObjects];                }                [arr addObject:controller];                [[weexInstance.viewController navigationController] setViewControllers:arr];            } else {                NSLog(@"WXEventModule.m --- finish 导航里页面数量没有1个及以上,不移除所有页面,添加当前页面进导航");                [arr addObject:controller];                [[weexInstance.viewController navigationController] setViewControllers:arr];            }        }    } else {        [self dealCallback:callback isSuccess:NO log:@"不处理该跳转"];    }}- (NSString *)getRealUrlPath:(NSString *)url {    NSString *newURL = url;    NSString *sandboxPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];    NSString *tmpPrefix = @"weex://";    //if ([newURL hasPrefix:sandboxPath]) {    if ([newURL hasPrefix:tmpPrefix]) {        //本地文件        //newURL = [NSString stringWithFormat:@"file://%@", url];        newURL = [newURL substringFromIndex:tmpPrefix.length]; //去除前缀标识        BOOL isHas = NO;        if ([[sandboxPath substringFromIndex:sandboxPath.length-1] isEqualToString:@"/"]) {            //最后有/            isHas = YES;            sandboxPath = [sandboxPath substringToIndex:sandboxPath.length-1]; //去除最后的/        }        if ([[newURL substringToIndex:1] isEqualToString:@"/"]) {            //前面有/,不用重复添加        } else {            //前面没有/,需要添加/            newURL = [NSString stringWithFormat:@"/%@", newURL];        }        //组装文件路径        newURL = [NSString stringWithFormat:@"file://%@%@", sandboxPath, newURL]; //拼装成沙盒全路径    } else if ([newURL hasPrefix:@"file://"]) {        //本地文件,不用处理    } else {        //在线文件        if ([url hasPrefix:@"//"]) {            newURL = [NSString stringWithFormat:@"http:%@", url];        } else if (![url hasPrefix:@"http"]) {            // relative path            newURL = [NSURL URLWithString:url relativeToURL:weexInstance.scriptURL].absoluteString;        }    }    return newURL;}- (BOOL)checkPageIsCanJump:(NSString *)fileName url:(NSString *)url fileMD5:(NSString *)fileMD5 {    //判断本地文件的MD5    BOOL isCanJump = YES;    NSString *localFilePrefix = @"file://";    if ([url hasPrefix:localFilePrefix] && fileMD5 && fileMD5.length > 0) {        //本地文件        NSString *filePath = [url substringFromIndex:localFilePrefix.length];        //要将后面?xxx=xxx的去掉        NSRange r = [filePath rangeOfString:@"?"];        if (r.location > 0 && r.location < filePath.length && r.length > 0) {            filePath = [filePath substringToIndex:r.location];        }        NSString *locaMD5 = [NSString fileMD5:filePath];        NSLog(@"fileMD5=%@", fileMD5);        NSLog(@"locaMD5=%@", locaMD5);        if ([fileMD5 isEqualToString:locaMD5]) {            //MD5值一致,文件没被篡改,可以执行            NSLog(@"!!!!!!!! !!!!!! !!! !! !MD5值一致,文件没被篡改,可以执行");            isCanJump = YES;        } else {            //MD5值不一致,文件被篡改,不执行            NSLog(@"!!!!!!!! !!!!!! !!! !! !MD5值不一致,文件被篡改,不执行");            isCanJump = NO;        }    } else {        NSLog(@"不是本地文件,或传入MD5为空,不进行MD5判断");    }    return isCanJump;}//获取当前页面的fileNameWX_EXPORT_METHOD(@selector(getCurrentPageFileName:))- (void)getCurrentPageFileName:(WXModuleCallback)callback {    NSString *isSuccess = @"false";    NSString *fileName = @"";    UIViewController *vc = [[weexInstance.viewController navigationController].viewControllers lastObject];    if (vc) {        if ([vc isKindOfClass:[LoaderViewController class]]) {            fileName = ((LoaderViewController *)vc).fileName ? ((LoaderViewController *)vc).fileName : @"";        } else if ([vc isKindOfClass:[LandscapeLoaderViewController class]]) {            fileName = ((LandscapeLoaderViewController *)vc).fileName ? ((LandscapeLoaderViewController *)vc).fileName : @"";        }        if (fileName.length > 0) {            isSuccess = @"true";        }    }    if (callback) {        callback(@{ @"isSuccess": isSuccess, @"fileName": fileName });    }}//更新当前页面WX_EXPORT_METHOD(@selector(refreshCurrectPage:callback:))- (void)refreshCurrectPage:(NSDictionary *)params callback:(WXModuleCallback)callback {    NSString *fileName = [params objectForKey:@"fileName"];    NSString *url = [params objectForKey:@"url"];    NSString *fileMD5 = [params objectForKey:@"fileMD5"];    //url = @"weex:///web/dist/my.js";    //fileMD5 = @"F280AE512A7F1F6D4E7ABF076C0E61A8";    NSString *newURL = [self getRealUrlPath:url];    UIViewController *vc = [[weexInstance.viewController navigationController].viewControllers lastObject];    [self refreshPage:vc fileName:fileName url:newURL fileMD5:fileMD5 callback:callback];}//更新导航里所有页面WX_EXPORT_METHOD(@selector(refreshAllPages:callback:))- (void)refreshAllPages:(NSDictionary *)params callback:(WXModuleCallback)callback {    NSArray *fileNames = [params objectForKey:@"fileNames"];    NSArray *urls = [params objectForKey:@"urls"];    NSArray *fileMD5s = [params objectForKey:@"fileMD5s"];    NSArray *vcs = [weexInstance.viewController navigationController].viewControllers;    if (vcs && [vcs count] > 0) {        for (NSInteger j = [vcs count]-1; j >= 0; j--) {            NSString *n = @"";            UIViewController *v = [vcs objectAtIndex:j];            if (v) {                if ([v isKindOfClass:[LoaderViewController class]]) {                    n = ((LoaderViewController *)v).fileName;                } else if ([v isKindOfClass:[LandscapeLoaderViewController class]]) {                    n = ((LandscapeLoaderViewController *)v).fileName;                }                if (n.length > 0) {                    //处理                    for (NSInteger i = [fileNames count]-1; i >= 0; i--) {                        NSString *fileName = [fileNames objectAtIndex:i];                        NSString *url = [urls objectAtIndex:i];                        if ([n isEqualToString:fileName]) {                            //匹配上了,需要更新该v                            NSString *newURL = [self getRealUrlPath:url];                            [self refreshPage:v fileName:fileName url:newURL fileMD5:[fileMD5s objectAtIndex:i] callback:^(id result) {                                //                            }];                            break;                        }                    }                } else {                    //不处理                    continue;                }            } else {                //不处理                continue;            }        }    }}- (void)refreshPage:(UIViewController *)vc fileName:(NSString *)fileName url:(NSString *)url fileMD5:(NSString *)fileMD5 callback:(WXModuleCallback)callback {    //判断文件是否可以跳转    BOOL isCanRefresh = [self checkPageIsCanJump:fileName url:url fileMD5:fileMD5];    //是否需要判断,过滤掉不是我们域名的url???    //    NSString *dealString = @"跳转处理";    NSString *undealString = @"不处理该跳转";    BOOL isSuccess = NO;    BOOL isLandscape = NO;    if (isCanRefresh) {        if (vc) {            if ([vc isKindOfClass:[LoaderViewController class]]) {                if ([((LoaderViewController *)vc).fileName isEqualToString:fileName]) {                    isSuccess = YES;                }            } else if ([vc isKindOfClass:[LandscapeLoaderViewController class]]) {                isLandscape = YES;                if ([((LandscapeLoaderViewController *)vc).fileName isEqualToString:fileName]) {                    isSuccess = YES;                }            }        }    }    //先回调通知B端    [self dealCallback:callback isSuccess:isSuccess log:(isSuccess ? dealString : undealString)];    //再处理页面刷新    if (isSuccess) {        if (!isLandscape) {            [((LoaderViewController *)vc) refreshPageWithUrl:url];        } else {            [((LandscapeLoaderViewController *)vc) refreshPageWithUrl:url];        }    }}//下载文件WX_EXPORT_METHOD(@selector(downloadFiles:callback:))- (void)downloadFiles:(NSDictionary *)params callback:(WXModuleKeepAliveCallback)callback {    NSArray *files = params[@"files"];    NSString *serverUrl = params[@"serverUrl"];    BOOL isUnzip = NO;    if (params[@"isUnzip"]) {        NSNumber *num = (NSNumber *)params[@"isUnzip"];        if (num) {            isUnzip = [num boolValue];        }    }    [[DownloadUtil sharedInstance]downloadFiles:files                                      serverUrl:serverUrl                                        isUnzip:isUnzip                                          block:^(BOOL result, NSInteger successCount, NSInteger allCount, NSArray *savePaths, BOOL isUnziped) {        callback(@{ @"result": @"success",@"data":@{ @"isSuccess": [NSNumber numberWithBool:result], @"successCount": [NSNumber numberWithInteger:successCount], @"allCount": [NSNumber numberWithInteger:allCount], @"savePaths": savePaths, @"isUnziped": [NSNumber numberWithBool:isUnziped]}}, false);    }                                  progressBlock:^(double progress) {        callback(@{ @"result": @"progress", @"data": @{ @"progress": @(progress) } }, true);    }];}//解压文件WX_EXPORT_METHOD(@selector(unzipFile:callback:))- (void)unzipFile:(NSString *)file callback:(WXModuleKeepAliveCallback)callback {    [[ZipUtil sharedInstance] unzipFile:file block:^(BOOL result) {        callback(@{ @"result": @"success", @"data": @{ @"isSuccess": [NSNumber numberWithBool:result]}}, false);    }];}//获取全局缓存WX_EXPORT_METHOD(@selector(getGlobelCaches:))- (void)getGlobelCaches:(WXModuleCallback)callback {    NSDictionary *dic = [AppDelegate shareInstance].globelCaches;    if (dic && dic.count > 0) {        callback(@{ @"result": @"success", @"data": [AppDelegate shareInstance].globelCaches });    } else {        //获取为空,B端已经做了从storage中获取的处理,此处理不处理        callback(@{ @"result": @"success", @"data": [AppDelegate shareInstance].globelCaches });    }}//设置/更新全局缓存WX_EXPORT_METHOD(@selector(setGlobelCaches:callback:))- (void)setGlobelCaches:(NSDictionary *)params callback:(WXModuleCallback)callback {    [AppDelegate shareInstance].globelCaches = [[NSMutableDictionary alloc]initWithDictionary:params];    //B端已经做了从storage更新数据的处理,此处不处理    callback(@{ @"result": @"success", @"data": [AppDelegate shareInstance].globelCaches });}//获取Web版本号WX_EXPORT_METHOD(@selector(getWebVersion:))- (void)getWebVersion:(WXModuleCallback)callback {    NSString *version = [AppDelegate shareInstance].globelWebVersion;    if (version && version.length > 0) {        callback(@{ @"result": @"success", @"data": version });    } else {        //获取为空,B端已经做了从storage中获取的处理,此处理不处理        callback(@{ @"result": @"success", @"data": @"" });    }}//设置/更新Web版本号WX_EXPORT_METHOD(@selector(setWebVersion:callback:))- (void)setWebVersion:(NSString *)version callback:(WXModuleCallback)callback {    [AppDelegate shareInstance].globelWebVersion = version;    //B端已经做了从storage更新数据的处理,此处不处理    callback(@{ @"result": @"success", @"data": [AppDelegate shareInstance].globelWebVersion });}

公用缓存

在C端定义整个app生命周期的变量,提供给B端,即可减轻weex大量的storage操作。

在AppDelegate.h中定义:

@property (strong, nonatomic) NSMutableDictionary *globelCaches;@property (strong, nonatomic) NSString *globelWebVersion;+(AppDelegate*)shareInstance;

在AppDelegate.m中声明:

+ (AppDelegate *)shareInstance {    return (AppDelegate *)[[UIApplication sharedApplication] delegate];}- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];    self.window.backgroundColor = [UIColor whiteColor];    [WeexSDKManager setup];    [self.window makeKeyAndVisible];    // Override point for customization after application launch.    self.globelCaches = [[NSMutableDictionary alloc]init];    return YES;}

谁来帮忙下载?

交给DownloadUtil!!

下载辅助类,结合传参isUnzip,若只有一个zip文件下载,isUnzip=ture,则会进行解压操作处理,具体B端传参决定。
下载过程中,会回调进度给调用方。

////  DownloadUtil.h//#import <Foundation/Foundation.h>#import "Singleton.h"typedef void(^DownloadUtilBlock)(BOOL result, NSInteger successCount, NSInteger allCount, NSArray *savePath, BOOL isUnziped);typedef void(^DownloadUtilProgressBlock)(double progress);@interface DownloadUtil : NSObjectAS_SINGLETON(DownloadUtil)- (void)downloadFiles:(NSArray *)files            serverUrl:(NSString *)serverUrl              isUnzip:(BOOL)isUnzip                block:(DownloadUtilBlock)block        progressBlock:(DownloadUtilProgressBlock)progressBlock;@end
////  DownloadUtil.m//#import "DownloadUtil.h"#import "FileUtil.h"#import "NSString+Extend.h"#import "ZipUtil.h"//web目录名称static NSString *WEB_LOCAL_FOLDER = @"BRLF_WEB";@interface DownloadUtil()@property (nonatomic) BOOL isUnzip;@property (nonatomic, strong) NSString *serverUrl;@property (nonatomic, strong) NSArray *downloadFiles;@property (nonatomic) DownloadUtilBlock downloadBlock;@property (nonatomic) DownloadUtilProgressBlock progressBlock;@end@implementation DownloadUtilDEF_SINGLETON(DownloadUtil);- (void)downloadFiles:(NSArray *)files            serverUrl:(NSString *)serverUrl              isUnzip:(BOOL)isUnzip                block:(DownloadUtilBlock)block        progressBlock:(DownloadUtilProgressBlock)progressBlock {    self.isUnzip = isUnzip;    self.serverUrl = serverUrl;    self.downloadFiles = files;    self.downloadBlock = block;    self.progressBlock = progressBlock;    [self performSelector:@selector(deal) withObject:nil afterDelay:0.1];}- (void)deal {    NSArray *files = self.downloadFiles;    DownloadUtilBlock block = self.downloadBlock;    DownloadUtilProgressBlock progress = self.progressBlock;    BOOL unzipFile = self.isUnzip;    __weak DownloadUtil *weakSelf = self;    __block NSInteger successCount = 0;    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{        //将文件下载到沙盒中        NSMutableArray *savePaths = [[NSMutableArray alloc]init];//        NSInteger successCount = 0;        for (NSInteger i = 0; i < files.count; i++) {//            NSString *file = [files objectAtIndex:i];            NSDictionary *dic = [files objectAtIndex:i];            NSString *fileName = dic[@"fileName"];            NSString *downloadUrl = dic[@"downloadUrl"];            NSString *savePath = [weakSelf dealDownloadFile:downloadUrl index:i];            if (savePath && savePath.length > 0) {                NSString *ex = @"";                if (savePath.length > 4) {                    ex = [savePath substringFromIndex:savePath.length-4];                }                NSLog(@"ex=%@", ex);                if (unzipFile && [ex isEqualToString:@".zip"] && files.count == 1) {                    //特殊处理zip                    NSLog(@"特殊处理zip");                    //只处理一个zip的情况,多zip不适用!!!!                    NSLog(@"只处理一个zip的情况,多zip不适用!!!!");                    dispatch_async(dispatch_get_main_queue(), ^{                        BOOL isHasOneFile = NO;                        if (files.count == 1) {                            //只有一个文件的情况                            isHasOneFile = YES;                            successCount += 1;                            //回调进度                            if (progress) {                                double p = (double)((double)successCount / (double)(files.count + 1));                                progress(p);                            }                        }                        NSString *file = savePath;                        [[ZipUtil sharedInstance] unzipFile:file block:^(BOOL unzipResult) {                            if (unzipResult) {                                //解压成功                                NSLog(@"解压成功");                                NSDictionary *sdic = @{@"fileName": fileName, @"savePath": savePath};                                [savePaths addObject:sdic];                                successCount += 1;                            } else {                                //解压失败                                NSLog(@"解压失败");                            }                            //回调进度                            if (progress) {                                if (isHasOneFile) {                                    double p = (double)((double)successCount / (double)(files.count + 1));                                    progress(p);                                } else {                                    double p = (double)((double)successCount / (double)files.count);                                    progress(p);                                }                            }                            //回调反馈结果                            if (isHasOneFile) {                                if (successCount > files.count) {                                    successCount = files.count;                                }                            }                            BOOL result = NO;                            if (successCount == files.count) {                                result = YES;                            }                            if (block) {                                block(result, successCount, files.count, savePaths, unzipResult);                            }                        }];                    });                    return;                } else {                    NSDictionary *sdic = @{@"fileName": fileName, @"savePath": savePath};                    [savePaths addObject:sdic];                    successCount += 1;                }            }            //回调进度            if (progress) {                double p = (double)((double)successCount / (double)files.count);                progress(p);            }        }        //回调反馈结果        BOOL result = NO;        if (successCount == files.count) {            result = YES;        }        if (block) {            block(result, successCount, files.count, savePaths, NO);        }    });}- (NSString *)dealDownloadFile:(NSString *)file index:(NSInteger)index {    NSString *savePath = @"";    BOOL isDownOk = NO;    //下载文件到沙盒中    NSLog(@"=====================dealDownloadFile[%ld] %@", (long)index, file);    //有下载地址,可以下载处理    NSLog(@"正在下载第%ld个文件[%@]", (long)index+1, file);    //下载处理    NSString *downloadUrl = [NSString stringWithFormat:@"%@%@", self.serverUrl, file];    NSLog(@"downlaodUrl=%@", downloadUrl);    NSData *verData = [NSData dataWithContentsOfURL:[NSURL URLWithString:downloadUrl]];    if (verData) {        NSLog(@"下载第%ld个文件[%@] 成功", (long)index+1, file);        //写文件        savePath = [FileUtil saveDataToSandBox:WEB_LOCAL_FOLDER fileName:file data:verData];        //test code        //打印文件的md5        //NSString *locaMD5 = [NSString fileMD5:savePath];        //NSLog(@"_________%@->MD5=%@", file, locaMD5);        //test code        /////        //取相对路径        NSString *sandboxPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];        savePath = [savePath substringFromIndex:sandboxPath.length];        /////        if (savePath && savePath.length > 0) {            //保存成功,下载文件成功            NSLog(@"保存第%ld个文件[%@] 成功", (long)index+1, file);            isDownOk = YES;        } else {            NSLog(@"保存第%ld个文件[%@] 失败", (long)index+1, file);        }    } else {        NSLog(@"下载第%ld个文件[%@] 失败", (long)index+1, file);    }//    return isDownOk;    return savePath;}@end

需要文件处理吗?

FileUtil

////  FileUtil.h//#import <Foundation/Foundation.h>#import "Singleton.h"@interface FileUtil : NSObjectAS_SINGLETON(FileUtil)/** *  @author guoli, 17-10-19 17:10:41 * *  @brief 保存数据到沙盒中 *  @param dir      沙盒中Documents下的目录名称 *  @param fileName 文件名称 *  @param data     要保存的数据 *  @return 保存的文件路径 //保存操作是否成功 */+ (NSString *)saveDataToSandBox:(NSString *)dir fileName:(NSString *)fileName data:(NSData *)data;/** *  @author guoli, 17-10-19 17:10:45 * *  @brief 创建目录 *  @param createDir 要创建的目录名称(路径) */+ (void)createFolder:(NSString *)createDir;/** *  @author guoli, 17-10-19 17:10:24 * *  @brief 删除沙盒中指定目录及其下所有文件 *  @param dir 目录名称(路径) */+ (void)deleteSandboxFolderAndAllFiles:(NSString *)dir;/** *  @author guoli, 17-10-19 17:10:22 * *  @brief 删除沙盒中指定文件(或目录) *  @param dir      目录名称(路径) *  @param fileName 文件名称(带后缀) */+ (void)deleteSandboxFile:(NSString *)dir fileName:(NSString *)fileName;/** *  @author guoli, 17-10-19 18:10:31 * *  @brief 检测目录是否存在 *  @param dir 目录名称(路径) *  @return true为存在,false为不存在 */+ (BOOL)checkFolderIsExisted:(NSString *)dir;/** *  @author guoli, 17-10-19 17:10:22 * *  @brief 复制指定路径下的文件到另一指定路径下 *  @param sourcePath 要复制的原文件所在路径 *  @param toPath     文件要复制到的所在路径 *  @return 文件复制操作是否成功 */+ (BOOL)copyMissingFile:(NSString *)sourcePath toPath:(NSString *)toPath;/** *  @author guoli, 17-10-19 17:10:46 * *  @brief 复制资源文件到沙盒中 *  @param resourceName 资源文件名称 *  @param type         资源文件类型(如: wav,mp3,png,jpg,plist,txt...) *  @param dir          沙盒Documents下的目录路径 *  @return 复制操作是否成功 */+ (BOOL)copyResourcesToSandbox:(NSString *)resourceName type:(NSString *)type dir:(NSString *)dir;/** *  @author guoli, 17-10-19 17:10:59 * *  @brief 获取沙盒里的文件绝对路径 *  @param dir      沙盒Documents下的目录路径 *  @param fileName 文件名称 *  @return 返回沙盒里该文件的绝对路径 */+ (NSString *)getSandBoxFilePath:(NSString *)dir fileName:(NSString *)fileName;/** *  @author guoli, 17-10-19 17:10:59 * *  @brief 从沙盒指定文件中获取Dic字典数据 *  @param dir      沙盒Documents下的目录路径 *  @param fileName 文件名称 *  @return 返回沙盒里该文件的Dic字典数据 */+ (NSDictionary *)getJsonDicData:(NSString *)dir fileName:(NSString *)fileName;/** *  @author guoli, 17-10-19 17:10:51 * *  @brief 获取文件的真实路由 *  @param fileName   文件名称 *  @param baseUrlStr 路由前缀 *  @return 返回包含路由前缀的文件的真实路由 */+ (NSString *)getRealUrlString:(NSString *)fileName baseUrlStr:(NSString *)baseUrlStr;@end
////  FileUtil.m//#import "FileUtil.h"@implementation FileUtilDEF_SINGLETON(FileUtil);/** *  @author guoli, 17-10-19 17:10:41 * *  @brief 保存数据到沙盒中 *  @param dir      沙盒中Documents下的目录名称 *  @param fileName 文件名称 *  @param data     要保存的数据 *  @return 保存的文件路径 //保存操作是否成功 */+ (NSString *)saveDataToSandBox:(NSString *)dir fileName:(NSString *)fileName data:(NSData *)data {    NSString *savePath = @"";    BOOL result = NO;    if (data) {        if (fileName && fileName.length > 0) {            NSString *sandboxPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];            NSString *dirPath = @"";            if (dir && dir.length > 0) {                dirPath = [sandboxPath stringByAppendingString:[NSString stringWithFormat:@"/%@", dir]];            } else {                dirPath = sandboxPath;            }            //创建目录            //NSLog(@"dirPath=%@", dirPath);            [FileUtil createFolder:dirPath];            //创建文件名称中所涉及到的目录            NSString *tmpDirPath = dirPath;            NSArray *arr = [fileName componentsSeparatedByString:@"/"];            for (NSInteger i = 0; i < arr.count; i++) {                NSString *tmpDir = [arr objectAtIndex:i];                if (i == arr.count-1) {                    NSRange r = [tmpDir rangeOfString:@"."];                    if (r.location > 0) {                        break;                    }                }                NSString *tmpPath = [NSString stringWithFormat:@"%@/%@", tmpDirPath, tmpDir];                //NSLog(@"tmpPath=%@", tmpPath);                [FileUtil createFolder:tmpPath];                tmpDirPath = tmpPath;            }            NSString *filePath = [NSString stringWithFormat:@"%@/%@", dirPath, fileName];            //NSLog(@"filePath=%@", filePath);            result = [data writeToFile:filePath atomically:YES];            //NSLog(@"result=%@", result ? @"YES" : @"NO");            if (result) {                savePath = filePath;            }        }    }//    return result;    return savePath;}/** *  @author guoli, 17-10-19 17:10:45 * *  @brief 创建目录 *  @param createDir 要创建的目录名称(路径) */+ (void)createFolder:(NSString *)createDir {    NSFileManager *fileManager = [NSFileManager defaultManager];    BOOL existed = [fileManager fileExistsAtPath:createDir];    if (!existed) {        NSError *error = nil;        [fileManager createDirectoryAtPath:createDir withIntermediateDirectories:YES attributes:nil error:&error];    }}/** *  @author guoli, 17-10-19 17:10:24 * *  @brief 删除沙盒中指定目录及其下所有文件 *  @param dir 目录名称(路径) */+ (void)deleteSandboxFolderAndAllFiles:(NSString *)dir {    NSString *sandboxPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];    NSFileManager *fileManager = [NSFileManager defaultManager];    NSString *myDirectory = [NSString stringWithFormat:@"%@/%@", sandboxPath, dir];    NSArray *fileArray = [fileManager subpathsAtPath:myDirectory];    if (fileArray && fileArray.count > 0) {        for (NSInteger i = fileArray.count - 1; i >= 0; i--) {            NSString *fn = [fileArray objectAtIndex:i];            NSString *fp = [NSString stringWithFormat:@"%@/%@", myDirectory, fn];            NSLog(@"%@", fp);            NSError *error = nil;            [fileManager removeItemAtPath:fp error:&error];        }    }}/** *  @author guoli, 17-10-19 17:10:22 * *  @brief 删除沙盒中指定文件(或目录) *  @param dir      目录名称(路径) *  @param fileName 文件名称(带后缀) */+ (void)deleteSandboxFile:(NSString *)dir fileName:(NSString *)fileName {    NSString *realPath = @"";    NSString *sandboxPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];    if (dir && dir.length > 0) {        if (fileName && fileName.length > 0) {            realPath = [sandboxPath stringByAppendingString:[NSString stringWithFormat:@"/%@/%@", dir, fileName]];        } else {            realPath = [sandboxPath stringByAppendingString:[NSString stringWithFormat:@"/%@", dir]];        }    } else {        if (fileName && fileName.length > 0) {            realPath = [sandboxPath stringByAppendingString:[NSString stringWithFormat:@"/%@", fileName]];        } else {            NSLog(@"文件名为空,不处理!");        }    }    if (realPath && realPath.length > 0) {        NSError *error = nil;        [[NSFileManager defaultManager]removeItemAtPath:realPath error:&error];    }}/** *  @author guoli, 17-10-19 18:10:31 * *  @brief 检测目录是否存在 *  @param dir 目录名称(路径) *  @return true为存在,false为不存在 */+ (BOOL)checkFolderIsExisted:(NSString *)dir {    BOOL result = NO;    NSString *sandboxPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];    NSString *webPath = @"";    if (dir && dir.length > 0) {        webPath = [sandboxPath stringByAppendingString:[NSString stringWithFormat:@"/%@/", dir]];    } else {        webPath = [sandboxPath stringByAppendingString:@"/"];    }    result = [[NSFileManager defaultManager] fileExistsAtPath:webPath];    return result;}/** *  @author guoli, 17-10-19 17:10:22 * *  @brief 复制指定路径下的文件到另一指定路径下 *  @param sourcePath 要复制的原文件所在路径 *  @param toPath     文件要复制到的所在路径 *  @return 文件复制操作是否成功 */+ (BOOL)copyMissingFile:(NSString *)sourcePath toPath:(NSString *)toPath {    BOOL result = YES;    NSString *finalLocation = [[toPath stringByAppendingPathComponent:sourcePath] lastPathComponent];    NSFileManager *fileManager = [NSFileManager defaultManager];    if (![fileManager fileExistsAtPath:finalLocation]) {        NSError *error = nil;        [fileManager copyItemAtPath:sourcePath toPath:toPath error:&error];        if (error) {            result = NO;            NSLog(@"copyMissingFile error:%ld, %@", (long)error.code, error.userInfo);        }    }    return result;}/** *  @author guoli, 17-10-19 17:10:46 * *  @brief 复制资源文件到沙盒中 *  @param resourceName 资源文件名称 *  @param type         资源文件类型(如: wav,mp3,png,jpg,plist,txt...) *  @param dir          沙盒Documents下的目录路径 *  @return 复制操作是否成功 */+ (BOOL)copyResourcesToSandbox:(NSString *)resourceName type:(NSString *)type dir:(NSString *)dir {    BOOL result = NO;    NSString *bundlePath = [[NSBundle mainBundle] pathForResource:resourceName ofType:type];    NSString *sandboxPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];    NSString *webPath = @"";    if (dir && dir.length > 0) {        webPath = [sandboxPath stringByAppendingString:[NSString stringWithFormat:@"/%@", dir]];    } else {        webPath = sandboxPath;    }    [FileUtil createFolder:webPath];    result = [FileUtil copyMissingFile:bundlePath toPath:webPath];    return result;}/** *  @author guoli, 17-10-19 17:10:59 * *  @brief 获取沙盒里的文件绝对路径 *  @param dir      沙盒Documents下的目录路径 *  @param fileName 文件名称 *  @return 返回沙盒里该文件的绝对路径 */+ (NSString *)getSandBoxFilePath:(NSString *)dir fileName:(NSString *)fileName {    NSString *webPath = @"";    NSString *sandboxPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];    if (dir && dir.length > 0) {        if ([dir hasSuffix:@"/"]) {            dir = [dir substringToIndex:dir.length-1];        }        if (fileName && fileName.length > 0) {            if ([fileName hasPrefix:@"/"]) {                fileName = [fileName substringFromIndex:1];            }            webPath = [sandboxPath stringByAppendingString:[NSString stringWithFormat:@"/%@/%@", dir, fileName]];        } else {            webPath = [sandboxPath stringByAppendingString:[NSString stringWithFormat:@"/%@", dir]];        }    } else {        if (fileName && fileName.length > 0) {            if ([fileName hasPrefix:@"/"]) {                fileName = [fileName substringFromIndex:1];            }            webPath = [sandboxPath stringByAppendingString:[NSString stringWithFormat:@"/%@", fileName]];        } else {            webPath = [sandboxPath stringByAppendingString:@"/"];        }    }    return webPath;}/** *  @author guoli, 17-10-19 17:10:59 * *  @brief 从沙盒指定文件中获取Dic字典数据 *  @param dir      沙盒Documents下的目录路径 *  @param fileName 文件名称 *  @return 返回沙盒里该文件的Dic字典数据 */+ (NSDictionary *)getJsonDicData:(NSString *)dir fileName:(NSString *)fileName {    NSDictionary *jsonDic = nil;    //从沙盒中获取数据    NSString *sandboxPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];    NSString *d = dir.length > 0 ? [NSString stringWithFormat:@"/%@", dir] : @"/";    NSString *webPath = [sandboxPath stringByAppendingString:d];    NSString *finalLocation = [webPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.json", fileName]].lastPathComponent;    NSURL *jsUrl = [NSURL fileURLWithPath:finalLocation];    //josn转对象    NSData *jsonData = [NSData dataWithContentsOfURL:jsUrl];    if (jsonData) {        NSError *error = nil;        NSDictionary *jsonObj = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:&error];        if (jsonObj && !error) {            jsonDic = jsonObj;        }    }    return jsonDic;}/** *  @author guoli, 17-10-19 17:10:51 * *  @brief 获取文件的真实路由 *  @param fileName   文件名称 *  @param baseUrlStr 路由前缀 *  @return 返回包含路由前缀的文件的真实路由 */+ (NSString *)getRealUrlString:(NSString *)fileName baseUrlStr:(NSString *)baseUrlStr {    NSString *filePath = @"";    if ([fileName hasPrefix:baseUrlStr]) {        filePath = fileName;    } else {        filePath = [NSString stringWithFormat:@"%@%@", baseUrlStr, fileName];    }    return filePath;}@end

需要解压么?

ZipUtil
需要在pod中导入SSZipArchive

def common    pod 'SSZipArchive'end

具体代码:

////  ZipUtil.h//#import <Foundation/Foundation.h>#import "Singleton.h"typedef void(^ZipUtilBlock)(BOOL result);@interface ZipUtil : NSObjectAS_SINGLETON(ZipUtil)- (void)unzipFile:(NSString *)zipFile block:(ZipUtilBlock)block;@end
////  ZipUtil.m//  #import "ZipUtil.h"#import "FileUtil.h"#import "ZipArchive.h"//web目录名称static NSString *WEB_LOCAL_FOLDER = @"BRLF_WEB";@interface ZipUtil()@property (nonatomic) ZipUtilBlock zipBlock;@end@implementation ZipUtilDEF_SINGLETON(ZipUtil);- (void)unzipFile:(NSString *)zipFile block:(ZipUtilBlock)block {    self.zipBlock = block;    [self performSelector:@selector(start:) withObject:zipFile afterDelay:0.1];}- (void)start:(NSString *)zipFile {    __weak ZipUtil *weakSelf = self;    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{        //解压zipFile到沙盒中        [weakSelf unzipFileToSandBox:zipFile];    });}/** *  @author guoli, 17-10-19 16:10:34 * *  @brief 解压zipFile到沙盒中 */- (void)unzipFileToSandBox:(NSString *)zipFile {    NSString *webDir = WEB_LOCAL_FOLDER;//    NSString *zipPath = [FileUtil getSandBoxFilePath: [NSString stringWithFormat:@"\%@", webDir] fileName:zipFile];//    NSString *unzipPath = [FileUtil getSandBoxFilePath: [NSString stringWithFormat:@"\%@", webDir] fileName:@""];    NSString *zipPath = [FileUtil getSandBoxFilePath: @"" fileName:zipFile];    NSString *unzipPath = [FileUtil getSandBoxFilePath: [NSString stringWithFormat:@"\%@", webDir] fileName:@""];    BOOL result = [self unzipToPath:unzipPath zipPath:zipPath];    NSLog(@"解压:%@ %@", webDir, (result ? @"成功" : @"失败"));    if (self.zipBlock) {        self.zipBlock(result);    }}/** *  @author guoli, 17-10-19 18:10:29 * *  @brief 解压指定压缩文件到指定路径 *  @param unzipPath 解压后文件存放的路径 *  @param zipPath   压缩文件的路径 *  @return 解压成功 或 失败 */- (BOOL)unzipToPath:(NSString *)unzipPath zipPath:(NSString *)zipPath {    BOOL result = NO;    NSLog(@"zipPath=%@", zipPath);    NSLog(@"unzipPath=%@", unzipPath);    result = [SSZipArchive unzipFileAtPath:zipPath toDestination:unzipPath];    NSLog(@"解压操作:%@", (result ? @"成功" : @"失败"));    return result;}@end

附上扩展类:

NSString+Extend

////  NSString+Extend.h//#import <Foundation/Foundation.h>@interface NSString (Extend)- (NSString *)md5;//计算文件的MD5值+ (NSString *)fileMD5:(NSString *)path;@end
////  NSString+Extend.m//#import "NSString+Extend.h"#import <CommonCrypto/CommonDigest.h>@implementation NSString (Extend)- (NSString *)md5 {    const char *cStr = [self UTF8String];    unsigned char result[CC_MD5_DIGEST_LENGTH];    CC_MD5(cStr, (CC_LONG)strlen(cStr), result);    NSMutableString *hash = [NSMutableString string];    for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {        [hash appendFormat:@"%02X", result[i]];    }    return [hash uppercaseString];}//计算文件的MD5值+ (NSString *)fileMD5:(NSString *)path {    NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path];    if (handle == nil) {        return @"ERROR GETTING FILE MD5"; // file didnt exist    }    CC_MD5_CTX md5;    CC_MD5_Init(&md5);    NSUInteger blockSize = 5 * 1024;    BOOL done = NO;    while(!done) {        NSData* fileData = [handle readDataOfLength: blockSize ];        CC_MD5_Update(&md5, [fileData bytes], (CC_LONG)[fileData length]);        if( [fileData length] == 0 ) done = YES;    }    unsigned char digest[CC_MD5_DIGEST_LENGTH];    CC_MD5_Final(digest, &md5);    NSMutableString *hash = [NSMutableString string];    for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {        [hash appendFormat:@"%02X", digest[i]];    }    return [hash uppercaseString];}@end

UIColor+Extend

////  UIColor+Extend.h//#import <UIKit/UIKit.h>@interface UIColor (Extend)+ (UIColor *)colorFromHexRGB:(NSString *)inColorString;+ (UIColor *)colorFromHexRGB:(NSString *)inColorString alpha:(CGFloat)alpha;@end
////  UIColor+Extend.m//#import "UIColor+Extend.h"@implementation UIColor (Extend)+ (UIColor *)colorFromHexRGB:(NSString *)inColorString {    return [self colorFromHexRGB:inColorString alpha:1.0];}+ (UIColor *)colorFromHexRGB:(NSString *)inColorString alpha:(CGFloat)alpha {    if ([inColorString hasPrefix:@"#"]) {        inColorString = [inColorString substringFromIndex:1];    }    UIColor *result = nil;    unsigned int colorCode = 0;    unsigned char redByte, greenByte, blueByte;    if (nil != inColorString) {        NSScanner *scanner = [NSScanner scannerWithString:inColorString];        (void) [scanner scanHexInt:&colorCode]; // ignore error    }    redByte = (unsigned char) (colorCode >> 16);    greenByte = (unsigned char) (colorCode >> 8);    blueByte = (unsigned char) (colorCode); // masks off high bits    result = [UIColor              colorWithRed: (float)redByte / 0xff              green: (float)greenByte/ 0xff              blue: (float)blueByte / 0xff              alpha:alpha];    return result;}@end

好像也没什么好说的,所以就直接贴上代码。


(未完,持续更新中…)

原创粉丝点击