// // XPRoomFaceTool.m // xplan-ios // // Created by 冯硕 on 2022/3/9. // #import "XPRoomFaceTool.h" #import #import #import #define FileHashDefaultChunkSizeForReadingData 1024*8 // 8K NSString * const kRoomFaceVersion = @"kRoomFaceVersion"; @interface XPRoomFaceTool () ///解压缩之后 表情资源所在的位置 @property (nonatomic,copy) NSString *faceDirectory; ///重试的次数 @property (nonatomic,assign) NSInteger retryCount; @end @implementation XPRoomFaceTool + (instancetype)shareFaceTool { static dispatch_once_t onceToken; static XPRoomFaceTool * tool; dispatch_once(&onceToken, ^{ tool = [[XPRoomFaceTool alloc] init]; tool.retryCount = 0; }); return tool; } - (void)downFaceData { [self downFaceDataCompletion:nil]; } - (void)downFaceDataCompletion:(nullable void (^)(NSString * nullable))completion { //获取沙盒doucument路径 NSArray*pathsss =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES); NSString *documentsDirectory = [pathsss objectAtIndex:0]; NSString*faceDirectory = [documentsDirectory stringByAppendingPathComponent:@"Face"]; ///首先判断 本地的数据是否 需要更新了 NSString *version = [[NSUserDefaults standardUserDefaults]objectForKey: kRoomFaceVersion]; if (version == nil || version.integerValue < self.version.integerValue) {///本地没有保存的话 就走下载 if (self.zipUrl.length > 0 && [self.zipUrl hasPrefix:@"http"]) { NSURL *URL = [NSURL URLWithString:self.zipUrl]; NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; //请求 NSURLRequest *request = [NSURLRequest requestWithURL:URL]; //下载Task操作 [[manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) { } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) { //要求返回一个URL, 返回的这个URL就是文件的位置的路径 NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; NSString *path = [cachesPath stringByAppendingPathComponent:response.suggestedFilename]; return [NSURL fileURLWithPath:path]; } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) { if (error == nil) { NSString *filePathStr = [filePath path]; NSString *fileMD5Str = [self getFileMD5WithPath:filePathStr]; fileMD5Str = [fileMD5Str uppercaseString]; if (![self.zipMd5 isEqualToString:fileMD5Str]) { //MD5校验 如果不相等就重新下载 [self performSelector:@selector(downFaceData) withObject:nil afterDelay:3]; }else { // filePath就是你下载文件的位置,你可以解压,也可以直接拿来使用 NSString *imgFilePath = [filePath path]; NSLog(@"img == %@", imgFilePath); NSString *zipPath = imgFilePath; NSFileManager *fileManager = [NSFileManager defaultManager]; //删除老的数据 [[NSFileManager defaultManager] removeItemAtPath:faceDirectory error:nil]; [fileManager createDirectoryAtPath:faceDirectory withIntermediateDirectories:YES attributes:nil error:nil]; NSLog(@"test == %@",faceDirectory); //解压后的路径 dispatch_async(dispatch_get_global_queue(0, 0), ^{ //解压 [SSZipArchive unzipFileAtPath:zipPath toDestination:faceDirectory overwrite:YES password:nil progressHandler:^(NSString * _Nonnull entry, unz_file_info zipInfo, long entryNumber, long total) { } completionHandler:^(NSString * _Nonnull path, BOOL succeeded, NSError * _Nullable error) { if (error == nil) { self.retryCount = 0; ///如果解压完成的话 就保存一下值 [[NSUserDefaults standardUserDefaults]setObject:self.version forKey:kRoomFaceVersion]; self.faceDirectory = faceDirectory; if (completion) { completion(faceDirectory); } } else { self.retryCount ++; if (self.retryCount <=10) { [self downFaceData]; } if (completion) { completion(nil); } } }]; }); } } else { self.retryCount ++; if (self.retryCount <=10) { [self downFaceData]; } if (completion) { completion(nil); } } }] resume]; } else { if (completion) { completion(nil); } } } else { self.faceDirectory = faceDirectory; if (completion) { completion(faceDirectory); } } } - (NSString*)getFileMD5WithPath:(NSString*)path { return (__bridge NSString *)FileMD5HashCreateWithPath((__bridge CFStringRef)path,FileHashDefaultChunkSizeForReadingData); } CFStringRef FileMD5HashCreateWithPath(CFStringRef filePath, size_t chunkSizeForReadingData) { // Declare needed variables CFStringRef result = NULL; CFReadStreamRef readStream = NULL; // Get the file URL CFURLRef fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)filePath, kCFURLPOSIXPathStyle, (Boolean)false); CC_MD5_CTX hashObject; bool hasMoreData = true; bool didSucceed; if (!fileURL) goto done; // Create and open the read stream readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, (CFURLRef)fileURL); if (!readStream) goto done; didSucceed = (bool)CFReadStreamOpen(readStream); if (!didSucceed) goto done; // Initialize the hash object CC_MD5_Init(&hashObject); // Make sure chunkSizeForReadingData is valid if (!chunkSizeForReadingData) { chunkSizeForReadingData = FileHashDefaultChunkSizeForReadingData; } // Feed the data to the hash object while (hasMoreData) { uint8_t buffer[chunkSizeForReadingData]; CFIndex readBytesCount = CFReadStreamRead(readStream, (UInt8 *)buffer, (CFIndex)sizeof(buffer)); if (readBytesCount == -1)break; if (readBytesCount == 0) { hasMoreData =false; continue; } CC_MD5_Update(&hashObject,(const void *)buffer,(CC_LONG)readBytesCount); } // Check if the read operation succeeded didSucceed = !hasMoreData; // Compute the hash digest unsigned char digest[CC_MD5_DIGEST_LENGTH]; CC_MD5_Final(digest, &hashObject); // Abort if the read operation failed if (!didSucceed) goto done; // Compute the string result char hash[2 *sizeof(digest) + 1]; for (size_t i =0; i < sizeof(digest); ++i) { snprintf(hash + (2 * i),3, "%02x", (int)(digest[i])); } result = CFStringCreateWithCString(kCFAllocatorDefault, (const char *)hash, kCFStringEncodingUTF8); done: if (readStream) { CFReadStreamClose(readStream); CFRelease(readStream); } if (fileURL) { CFRelease(fileURL); } return result; } @end