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;
}
//获取当前页面的fileName
WX_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 : NSObject
AS_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 DownloadUtil
DEF_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 : NSObject
AS_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 FileUtil
DEF_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 : NSObject
AS_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 ZipUtil
DEF_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
好像也没什么好说的,所以就直接贴上代码。
(未完,持续更新中…)