CWFileStreamSeparation.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. //
  2. // CWFileStreamSeparation.m
  3. // uploadFileDemo
  4. //
  5. // Created by hyjet on 2018/3/9.
  6. // Copyright © 2018年 uploadFileDemo. All rights reserved.
  7. //
  8. #import "CWFileStreamSeparation.h"
  9. #import "CWFileManager.h"
  10. #import <CommonCrypto/CommonDigest.h>
  11. #import "objc/runtime.h"
  12. #pragma mark - CWFileStreamSeparation
  13. #define FileHashDefaultChunkSizeForReadingData 1024*8
  14. @interface CWFileStreamSeparation ()
  15. //@property (nonatomic, copy) NSString *fileName;
  16. //@property (nonatomic, assign) NSUInteger fileSize;
  17. //@property (nonatomic, strong) NSArray<CWStreamFragment*> *streamFragments;
  18. @property (nonatomic, strong) NSFileHandle *readFileHandle;
  19. @property (nonatomic, strong) NSFileHandle *writeFileHandle;
  20. @property (nonatomic, assign) BOOL isReadOperation;
  21. //@property (nonatomic,assign)double progressRate;
  22. //@property (nonatomic,assign)NSInteger uploadDateSize;
  23. @end
  24. @implementation CWFileStreamSeparation
  25. -(NSString *)description{
  26. unsigned int count;
  27. const char *clasName = object_getClassName(self);
  28. NSMutableString *string = [NSMutableString stringWithFormat:@"<%s: %p>:[ \n",clasName, self];
  29. Class clas = NSClassFromString([NSString stringWithCString:clasName encoding:NSUTF8StringEncoding]);
  30. Ivar *ivars = class_copyIvarList(clas, &count);
  31. for (int i = 0; i < count; i++) {
  32. @autoreleasepool {
  33. Ivar ivar = ivars[i];
  34. const char *name = ivar_getName(ivar);
  35. //得到类型
  36. NSString *type = [NSString stringWithCString:ivar_getTypeEncoding(ivar) encoding:NSUTF8StringEncoding];
  37. NSString *key = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
  38. id value = [self valueForKey:key];
  39. //确保BOOL 值输出的是YES 或 NO,这里的B是我打印属性类型得到的……
  40. if ([type isEqualToString:@"B"]) {
  41. value = (value == 0 ? @"NO" : @"YES");
  42. }
  43. [string appendFormat:@"\t%@: %@\n",[self delLine:key], value];
  44. }
  45. }
  46. [string appendFormat:@"]"];
  47. return string;
  48. }
  49. //因为ivar_getName得到的是一个带有下划线的名字,去掉下划线看起来更漂亮
  50. -(NSString *)delLine:(NSString *)string{
  51. if ([string hasPrefix:@"_"]) {
  52. return [string substringFromIndex:1];
  53. }
  54. return string;
  55. }
  56. + (NSString *)fileKey {
  57. CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
  58. CFStringRef cfstring = CFUUIDCreateString(kCFAllocatorDefault, uuid);
  59. const char *cStr = CFStringGetCStringPtr(cfstring,CFStringGetFastestEncoding(cfstring));
  60. unsigned char result[16];
  61. CC_MD5( cStr, (unsigned int)strlen(cStr), result );
  62. CFRelease(uuid);
  63. CFRelease(cfstring);
  64. return [NSString stringWithFormat:
  65. @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%08lx",
  66. result[0], result[1], result[2], result[3],
  67. result[4], result[5], result[6], result[7],
  68. result[8], result[9], result[10], result[11],
  69. result[12], result[13], result[14], result[15],
  70. (unsigned long)(arc4random() % NSUIntegerMax)];
  71. }
  72. + (NSString *)fileKeyMD5WithPath:(NSString*)path
  73. {
  74. return (__bridge_transfer NSString *)FileMD5HashCreateWithPath((__bridge CFStringRef)path, FileHashDefaultChunkSizeForReadingData);
  75. // NSString *fileName = [path lastPathComponent];
  76. // return [RSATool md5Encrypt:fileName];
  77. }
  78. CFStringRef FileMD5HashCreateWithPath(CFStringRef filePath,size_t chunkSizeForReadingData) {
  79. // Declare needed variables
  80. CFStringRef result = NULL;
  81. CFReadStreamRef readStream = NULL;
  82. // Get the file URL
  83. CFURLRef fileURL =
  84. CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
  85. (CFStringRef)filePath,
  86. kCFURLPOSIXPathStyle,
  87. (Boolean)false);
  88. if (!fileURL) goto done;
  89. // Create and open the read stream
  90. readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault,
  91. (CFURLRef)fileURL);
  92. if (!readStream) goto done;
  93. bool didSucceed = (bool)CFReadStreamOpen(readStream);
  94. if (!didSucceed) goto done;
  95. // Initialize the hash object
  96. CC_MD5_CTX hashObject;
  97. CC_MD5_Init(&hashObject);
  98. // Make sure chunkSizeForReadingData is valid
  99. if (!chunkSizeForReadingData) {
  100. chunkSizeForReadingData = FileHashDefaultChunkSizeForReadingData;
  101. }
  102. // Feed the data to the hash object
  103. bool hasMoreData = true;
  104. while (hasMoreData) {
  105. uint8_t buffer[chunkSizeForReadingData];
  106. CFIndex readBytesCount = CFReadStreamRead(readStream,(UInt8 *)buffer,(CFIndex)sizeof(buffer));
  107. if (readBytesCount == -1) break;
  108. if (readBytesCount == 0) {
  109. hasMoreData = false;
  110. continue;
  111. }
  112. CC_MD5_Update(&hashObject,(const void *)buffer,(CC_LONG)readBytesCount);
  113. }
  114. // Check if the read operation succeeded
  115. didSucceed = !hasMoreData;
  116. // Compute the hash digest
  117. unsigned char digest[CC_MD5_DIGEST_LENGTH];
  118. CC_MD5_Final(digest, &hashObject);
  119. // Abort if the read operation failed
  120. if (!didSucceed) goto done;
  121. // Compute the string result
  122. char hash[2 * sizeof(digest) + 1];
  123. for (size_t i = 0; i < sizeof(digest); ++i) {
  124. snprintf(hash + (2 * i), 3, "%02x", (int)(digest[i]));
  125. }
  126. result = CFStringCreateWithCString(kCFAllocatorDefault,(const char *)hash,kCFStringEncodingUTF8);
  127. done:
  128. if (readStream) {
  129. CFReadStreamClose(readStream);
  130. CFRelease(readStream);
  131. }
  132. if (fileURL) {
  133. CFRelease(fileURL);
  134. }
  135. return result;
  136. }
  137. - (void)encodeWithCoder:(NSCoder *)aCoder {
  138. [aCoder encodeObject:[self fileName] forKey:@"fileName"];
  139. [aCoder encodeObject:[self imageData] forKey:@"imageData"];
  140. [aCoder encodeObject:[NSNumber numberWithDouble:[self timeStamp]] forKey:@"timeStamp"];
  141. [aCoder encodeObject:[self uploadLocation] forKey:@"uploadLocation"];
  142. [aCoder encodeObject:[NSNumber numberWithUnsignedInteger:[self fileSize]] forKey:@"fileSize"];
  143. [aCoder encodeObject:[NSNumber numberWithInteger:[self fileStatus]] forKey:@"fileStatus"];
  144. [aCoder encodeObject:[self filePath] forKey:@"filePath"];
  145. [aCoder encodeObject:[self md5String] forKey:@"md5String"];
  146. [aCoder encodeObject:[self streamFragments] forKey:@"streamFragments"];
  147. [aCoder encodeObject:[self bizId] forKey:@"bizId"];
  148. [aCoder encodeObject:[NSNumber numberWithUnsignedInteger:[self uploadDateSize]] forKey:@"uploadDateSize"];
  149. [aCoder encodeObject:[NSNumber numberWithDouble:[self progressRate]] forKey:@"progressRate"];
  150. }
  151. - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
  152. self = [super init];
  153. if (self != nil) {
  154. [self setFileName:[aDecoder decodeObjectForKey:@"fileName"]];
  155. [self setImageData:[aDecoder decodeObjectForKey:@"imageData"]];
  156. [self setTimeStamp:[[aDecoder decodeObjectForKey:@"timeStamp"] integerValue]];
  157. [self setUploadLocation:[aDecoder decodeObjectForKey:@"uploadLocation"]];
  158. [self setFileStatus:[[aDecoder decodeObjectForKey:@"fileStatus"] intValue]];
  159. [self setFileSize:[[aDecoder decodeObjectForKey:@"fileSize"] unsignedIntegerValue]];
  160. [self setFilePath:[aDecoder decodeObjectForKey:@"filePath"]];
  161. [self setMd5String:[aDecoder decodeObjectForKey:@"md5String"]];
  162. [self setStreamFragments:[aDecoder decodeObjectForKey:@"streamFragments"]];
  163. [self setBizId:[aDecoder decodeObjectForKey:@"bizId"]];
  164. [self setProgressRate:[[aDecoder decodeObjectForKey:@"progressRate"] doubleValue]];
  165. [self setUploadDateSize:[[aDecoder decodeObjectForKey:@"uploadDateSize"] unsignedIntegerValue]];
  166. }
  167. return self;
  168. }
  169. - (BOOL)getFileInfoAtPath:(NSString*)path {
  170. NSFileManager *fileMgr = [NSFileManager defaultManager];
  171. if (![fileMgr fileExistsAtPath:path]) {
  172. HLog(@"文件不存在:%@",path);
  173. return NO;
  174. }
  175. self.filePath = path;
  176. NSDictionary *attr =[fileMgr attributesOfItemAtPath:path error:nil];
  177. self.fileSize = attr.fileSize;
  178. self.md5String = [CWFileStreamSeparation fileKeyMD5WithPath:path];
  179. self.bizId=[[NSUUID UUID] UUIDString];
  180. self.uploadDateSize = 0;
  181. self.progressRate = 0.00;
  182. NSString *fileName = [path lastPathComponent];
  183. self.fileName = fileName;
  184. self.fileStatus = CWUploadStatusWaiting;
  185. return YES;
  186. }
  187. - (BOOL)getFileInfoAtPath:(NSString*)path uploadLocation:(NSString *)uploadLocation imageData:(NSData *)imageData {
  188. NSFileManager *fileMgr = [NSFileManager defaultManager];
  189. if (![fileMgr fileExistsAtPath:path]) {
  190. HLog(@"文件不存在:%@",path);
  191. return NO;
  192. }
  193. self.filePath = path;
  194. self.uploadLocation = uploadLocation;
  195. self.imageData = imageData;
  196. self.timeStamp = [[NSDate date] timeIntervalSince1970];
  197. NSDictionary *attr =[fileMgr attributesOfItemAtPath:path error:nil];
  198. self.fileSize = attr.fileSize;
  199. self.md5String = [CWFileStreamSeparation fileKeyMD5WithPath:path];
  200. self.bizId=[[NSUUID UUID] UUIDString];
  201. self.uploadDateSize = 0;
  202. self.progressRate = 0.00;
  203. NSString *fileName = [path lastPathComponent];
  204. self.fileName = fileName;
  205. self.fileStatus = CWUploadStatusWaiting;
  206. return YES;
  207. }
  208. // 若为读取文件数据,打开一个已存在的文件。
  209. // 若为写入文件数据,如果文件不存在,会创建的新的空文件。
  210. - (instancetype)initFileOperationAtPath:(NSString*)path forReadOperation:(BOOL)isReadOperation {
  211. if (self = [super init]) {
  212. self.isReadOperation = isReadOperation;
  213. if (_isReadOperation) {
  214. if (![self getFileInfoAtPath:path]) {
  215. return nil;
  216. }
  217. self.readFileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
  218. [self cutFileForFragments];
  219. } else {
  220. NSFileManager *fileMgr = [NSFileManager defaultManager];
  221. if (![fileMgr fileExistsAtPath:path]) {
  222. [fileMgr createFileAtPath:path contents:nil attributes:nil];
  223. }
  224. if (![self getFileInfoAtPath:path]) {
  225. return nil;
  226. }
  227. self.writeFileHandle = [NSFileHandle fileHandleForWritingAtPath:path];
  228. }
  229. }
  230. return self;
  231. }
  232. // 若为读取文件数据,打开一个已存在的文件。
  233. // 若为写入文件数据,如果文件不存在,会创建的新的空文件。
  234. - (instancetype)initFileOperationAtPath:(NSString*)path forReadOperation:(BOOL)isReadOperation uploadLocation:(NSString *)uploadLocation imageData:(NSData *)imageData {
  235. if (self = [super init]) {
  236. self.isReadOperation = isReadOperation;
  237. if (_isReadOperation) {
  238. if (![self getFileInfoAtPath:path uploadLocation:uploadLocation imageData:imageData]) {
  239. return nil;
  240. }
  241. self.readFileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
  242. [self cutFileForFragments];
  243. } else {
  244. NSFileManager *fileMgr = [NSFileManager defaultManager];
  245. if (![fileMgr fileExistsAtPath:path]) {
  246. [fileMgr createFileAtPath:path contents:nil attributes:nil];
  247. }
  248. if (![self getFileInfoAtPath:path uploadLocation:uploadLocation imageData:imageData]) {
  249. return nil;
  250. }
  251. self.writeFileHandle = [NSFileHandle fileHandleForWritingAtPath:path];
  252. }
  253. }
  254. return self;
  255. }
  256. #pragma mark - 读操作
  257. //切分文件片段
  258. - (void)cutFileForFragments {
  259. NSUInteger offset = CWStreamFragmentMaxSize;
  260. // 块数
  261. NSUInteger chunks = (_fileSize%offset==0)?(_fileSize/offset):(_fileSize/(offset) + 1);
  262. NSMutableArray<CWStreamFragment *> *fragments = [[NSMutableArray alloc] initWithCapacity:0];
  263. for (NSUInteger i = 0; i < chunks; i ++) {
  264. CWStreamFragment *fFragment = [[CWStreamFragment alloc] init];
  265. fFragment.fragmentStatus = NO;
  266. fFragment.fragmentId = [[self class] fileKey];
  267. fFragment.fragementOffset = i * offset;
  268. if (i != chunks - 1) {
  269. fFragment.fragmentSize = offset;
  270. } else {
  271. fFragment.fragmentSize = _fileSize - fFragment.fragementOffset;
  272. }
  273. [fragments addObject:fFragment];
  274. }
  275. self.streamFragments = fragments;
  276. }
  277. //通过分片信息读取对应的片数据
  278. - (NSData*)readDateOfFragment:(CWStreamFragment*)fragment {
  279. HLog(@"del 2024525 暂未做");
  280. /* hxd del 2024525 暂未做
  281. if (self.readFileHandle==nil) {
  282. NSDictionary *loginDict = [[UseAccountManage shareInstance] getLoginInfo];
  283. if ([[loginDict allKeys] containsObject:@"account"])
  284. {
  285. NSString *fileFolder = kPath_YunPan_Upload_Folder;
  286. NSString *fileName = [_filePath lastPathComponent];
  287. NSString *fileURL = [fileFolder stringByAppendingPathComponent:fileName];
  288. self.readFileHandle = [NSFileHandle fileHandleForReadingAtPath:fileURL];
  289. }else {
  290. HLog(@"未登录 读取缓存文件失败!");
  291. }
  292. }
  293. if (fragment) {
  294. [self seekToFileOffset:fragment.fragementOffset];
  295. return [_readFileHandle readDataOfLength:fragment.fragmentSize];
  296. }
  297. [self closeFile];
  298. */
  299. return nil;
  300. }
  301. - (NSData*)readDataOfLength:(NSUInteger)bytes {
  302. return [_readFileHandle readDataOfLength:bytes];
  303. }
  304. - (NSData*)readDataToEndOfFile {
  305. return [_readFileHandle readDataToEndOfFile];
  306. }
  307. #pragma mark - 写操作
  308. // 写入文件数据
  309. - (void)writeData:(NSData *)data {
  310. [_writeFileHandle writeData:data];
  311. }
  312. #pragma mark - common
  313. // 获取当前偏移量
  314. - (NSUInteger)offsetInFile{
  315. if (_isReadOperation) {
  316. return [_readFileHandle offsetInFile];
  317. }
  318. return [_writeFileHandle offsetInFile];
  319. }
  320. // 设置偏移量,仅对读取设置
  321. - (void)seekToFileOffset:(NSUInteger)offset {
  322. [_readFileHandle seekToFileOffset:offset];
  323. }
  324. // 将偏移量定位到文件的末尾
  325. - (NSUInteger)seekToEndOfFile{
  326. if (_isReadOperation) {
  327. return (NSUInteger)[_readFileHandle seekToEndOfFile];
  328. }
  329. return [_writeFileHandle seekToEndOfFile];
  330. }
  331. // 关闭文件
  332. - (void)closeFile {
  333. if (_isReadOperation) {
  334. [_readFileHandle closeFile];
  335. } else {
  336. [_writeFileHandle closeFile];
  337. }
  338. }
  339. //归档
  340. + (void)archerTheDictionary:(NSDictionary *)dict file:(NSString *)path{
  341. NSData *data = [NSKeyedArchiver archivedDataWithRootObject:dict];
  342. BOOL finish = [data writeToFile:path atomically:YES];
  343. if (finish) HLog(@"归档成功");
  344. }
  345. //解档
  346. + (NSMutableDictionary *)unArcherThePlist:(NSString *)path{
  347. NSMutableDictionary *dic = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
  348. return dic;
  349. }
  350. @end
  351. @implementation CWStreamFragment
  352. - (void)encodeWithCoder:(NSCoder *)aCoder {
  353. [aCoder encodeObject:[self fragmentId] forKey:@"fragmentId"];
  354. [aCoder encodeObject:[NSNumber numberWithUnsignedInteger:[self fragmentSize]] forKey:@"fragmentSize"];
  355. [aCoder encodeObject:[NSNumber numberWithUnsignedInteger:[self fragementOffset]] forKey:@"fragementOffset"];
  356. [aCoder encodeObject:[NSNumber numberWithUnsignedInteger:[self fragmentStatus]] forKey:@"fragmentStatus"];
  357. }
  358. - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
  359. self = [super init];
  360. if (self != nil) {
  361. [self setFragmentId:[aDecoder decodeObjectForKey:@"fragmentId"]];
  362. [self setFragmentSize:[[aDecoder decodeObjectForKey:@"fragmentSize"] unsignedIntegerValue]];
  363. [self setFragementOffset:[[aDecoder decodeObjectForKey:@"fragementOffset"] unsignedIntegerValue]];
  364. [self setFragmentStatus:[[aDecoder decodeObjectForKey:@"fragmentStatus"] boolValue]];
  365. }
  366. return self;
  367. }
  368. @end