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
好像也没什么好说的,所以就直接贴上代码。
(未完,持续更新中…)
- weex更新方案探索(三)
- weex更新方案探索(一)
- weex更新方案探索(四)
- weex更新方案探索(五)
- weex更新方案探索(六)
- weex更新方案探索(七)
- weex更新方案探索(二)
- Weex list复用(三)
- android app热更新方案探讨(三)
- weex 踩坑记(持续更新中……)
- weex 在线js文件下载(更新)机制分析
- 软件探索(三)
- 阿里weex研究iOS(三)真机实时调试
- ROSjava探索(更新完结)
- weex sdk集成到Android工程三. weex服务项目搭建
- Dagger2(初步探索三)
- 腾讯开源手游热更新方案Xlua尝鲜(三)——C#访问Lua
- Weex使用(1)
- maven修改本地仓库,远程仓库与中央仓库
- Go-Ethereum 1.7.2 结合 Mist 0.9.2 实现代币智能合约的实例
- html编码规范
- 如何选择开源许可证?
- Eclipse中关于JRE System Library、Web App Libraries的疑惑
- weex更新方案探索(三)
- 嵌入式导论3:资源共享
- 斯坦福大学秋季课程《深度学习理论》STATS 385开讲
- Unit12
- uvc camera MTK平台调试总结
- CNN中的采样和池化
- 生产者消费者模式sychronized实现 java
- Ajax请求jsp页面中的对象数组
- c语言小程序