CWUploadTask.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. //
  2. // CWUploadTask.m
  3. // uploadFileDemo
  4. //
  5. // Created by hyjet on 2018/3/9.
  6. // Copyright © 2018年 uploadFileDemo. All rights reserved.
  7. //
  8. #import "CWUploadTask.h"
  9. #import "CWFileUploadManager.h"
  10. #import "CWFileStreamSeparation.h"
  11. #import "CWFileManager.h"
  12. #import "CWUploadTask+CheckInfo.h"
  13. //分隔符
  14. #define Boundary @"1a2b3c"
  15. //一般换行
  16. #define Wrap1 @"\r\n"
  17. //key-value换行
  18. #define Wrap2 @"\r\n\r\n"
  19. //开始分割
  20. #define StartBoundary [NSString stringWithFormat:@"--%@%@",Boundary,Wrap1]
  21. //文件分割完成
  22. #define EndBody [NSString stringWithFormat:@"--%@--",Boundary]
  23. //一个片段上传失败默认重试3次
  24. #define REPEAT_MAX 3
  25. //#define plistPath [[CWFileManager cachesDir] stringByAppendingPathComponent:uploadPlist]
  26. #define GETPOSTFLOWCHUNKAPI @"/nextcloud/index.php/apps/rcapi/ajax/upload.php"/*断点续传*/
  27. NSString *const CWUploadTaskExeing = @"TaskExeing";
  28. NSString *const CWUploadTaskExeError = @"TaskExeError";
  29. NSString *const CWUploadTaskExeEnd = @"TaskExeEnd";
  30. NSString *const CWUploadTaskExeSuspend = @"TaskExeSuspend";
  31. @interface CWUploadTask ()
  32. //@property (nonatomic,strong)NSURLSessionUploadTask *uploadTask;
  33. //@property (nonatomic,strong)NSMutableURLRequest *request;
  34. //@property (nonatomic,readwrite)NSURLSessionTaskState taskState;
  35. @property (nonatomic,readwrite)NSURL * url;
  36. @property (nonatomic,readwrite)NSString *ID;
  37. @property (nonatomic,readwrite)NSMutableDictionary *param;//上传时参数
  38. @property (nonatomic,readwrite)CWFileStreamSeparation *fileStream;
  39. @property (nonatomic,copy)finishHandler finishBlock;//片段上传成功上传的回调block
  40. @property (nonatomic,copy)success successBlock;//整体上传成功上传的回调block
  41. @property (nonatomic,copy)NSString *chunkNumName;//片段编号这一参数的参数名
  42. @property (nonatomic,copy)NSDictionary *lastParam;//片段完成上传后的参数
  43. @property (nonatomic,assign)NSInteger chunkNo;//片段完成上传后的编号
  44. @property (nonatomic,assign)NSInteger taskRepeatNum;//重试次数
  45. @property (nonatomic,assign)BOOL isSuspendedState;//记录状态更改
  46. @property (nonatomic,strong)CWFileUploadManager *uploadManager;
  47. @end
  48. @implementation CWUploadTask
  49. -(CWFileUploadManager *)uploadManager
  50. {
  51. if (!_uploadManager) {
  52. _uploadManager = [CWFileUploadManager shardUploadManager];
  53. }
  54. return _uploadManager;
  55. }
  56. - (void)setFileStream:(CWFileStreamSeparation *)fileStream
  57. {
  58. _fileStream.fileStatus = CWUploadStatusWaiting;
  59. _taskRepeatNum = 0;
  60. _ID = fileStream.md5String;
  61. for (NSInteger idx=0; idx<fileStream.streamFragments.count; idx++) {
  62. CWStreamFragment *fragment = fileStream.streamFragments[idx];
  63. if (!fragment.fragmentStatus) {
  64. _chunkNo = idx;
  65. }
  66. }
  67. _fileStream = fileStream;
  68. }
  69. + (NSMutableDictionary<NSString*,CWUploadTask*> *)uploadTasksWithDict:(NSDictionary<NSString*,CWFileStreamSeparation*> *)dict{
  70. NSMutableDictionary *taskDict = [NSMutableDictionary dictionary];
  71. for (NSString *key in dict.allKeys) {
  72. CWFileStreamSeparation *fs = [dict objectForKey:key];
  73. CWUploadTask *task = [CWUploadTask initWithStreamModel:fs];
  74. [taskDict setValue:task forKey:key];
  75. }
  76. return taskDict;
  77. }
  78. + (instancetype)initWithStreamModel:(CWFileStreamSeparation *)fileStream
  79. {
  80. CWUploadTask *task = [CWUploadTask new];
  81. task.fileStream = fileStream;
  82. task.timeStamp = [iTools getNowTimeStamp];
  83. task.isSuspendedState = NO;
  84. task.url = [CWFileUploadManager shardUploadManager].url;
  85. return task;
  86. }
  87. - (void)listenTaskExeCallback:(finishHandler _Nonnull)block success:(success)successBlock
  88. {
  89. self.finishBlock = block;
  90. self.successBlock = successBlock;
  91. if (_finishBlock) _finishBlock(_fileStream,nil);
  92. }
  93. - (instancetype _Nonnull)initWithStreamModel:(CWFileStreamSeparation *)fileStream finish:(finishHandler _Nonnull)block success:(success)successBlock
  94. {
  95. if (self = [super init]) {
  96. self.fileStream = fileStream;
  97. _finishBlock = block;
  98. _successBlock = successBlock;
  99. }
  100. return self;
  101. }
  102. //-(NSMutableURLRequest*)uploadRequest
  103. //{
  104. // if ([CWFileUploadManager shardUploadManager].request) {
  105. // _request = [CWFileUploadManager shardUploadManager].request;
  106. // }else{
  107. // HLog(@"请配置上传任务的request");
  108. // }
  109. // return _request;
  110. //
  111. //}
  112. -(NSData*)taskRequestBodyWithParam:(NSDictionary *)param uploadData:(NSData *)data
  113. {
  114. NSMutableData* totlData=[NSMutableData new];
  115. NSArray* allKeys=[param allKeys];
  116. for (int i=0; i<allKeys.count; i++)
  117. {
  118. NSString *disposition = [NSString stringWithFormat:@"%@Content-Disposition: form-data; name=\"%@\"%@",StartBoundary,allKeys[i],Wrap2];
  119. NSString* object=[param objectForKey:allKeys[i]];
  120. disposition =[disposition stringByAppendingString:[NSString stringWithFormat:@"%@",object]];
  121. disposition =[disposition stringByAppendingString:Wrap1];
  122. [totlData appendData:[disposition dataUsingEncoding:NSUTF8StringEncoding]];
  123. }
  124. NSString *body=[NSString stringWithFormat:@"%@Content-Disposition: form-data; name=\"picture\"; filename=\"%@\";Content-Type:video/mpeg4%@",StartBoundary,@"file",Wrap2];
  125. [totlData appendData:[body dataUsingEncoding:NSUTF8StringEncoding]];
  126. [totlData appendData:data];
  127. [totlData appendData:[Wrap1 dataUsingEncoding:NSUTF8StringEncoding]];
  128. [totlData appendData:[EndBody dataUsingEncoding:NSUTF8StringEncoding]];
  129. return totlData;
  130. }
  131. /**
  132. * GET:上传任务 获取上传文件相关信息
  133. */
  134. - (void)getFileInfo
  135. {
  136. __weak typeof(self) weekSelf = self;
  137. [self checkParamFromServer:self.fileStream paramCallback:^(NSString * _Nonnull chunkNumName, NSDictionary * _Nullable param) {
  138. weekSelf.chunkNumName = chunkNumName;
  139. weekSelf.param = [NSMutableDictionary dictionaryWithDictionary:param];
  140. [weekSelf startExe];
  141. }];
  142. }
  143. /**
  144. * POST:上传任务 获取上传文件相关信息
  145. */
  146. - (void)startExe{
  147. HLog(@"del 2024525 暂未做");
  148. /* hxd del 2024525 暂未做
  149. if (_isSuspendedState) {
  150. HLog(@"暂停上传");
  151. [self taskCancel];
  152. return;
  153. }
  154. //判断无参数的情况下先将文件信息上传并获得参数
  155. if (!_param) [self getFileInfo];
  156. dispatch_group_t group = dispatch_group_create();
  157. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  158. dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
  159. if (_fileStream.fileStatus == CWUploadStatusFinished && _successBlock) {
  160. _successBlock(_fileStream);
  161. [self sendNotionWithKey:CWUploadTaskExeEnd userInfo:@{@"fileStream":_fileStream}];
  162. return;
  163. };
  164. CWStreamFragment *fragment;
  165. for (NSInteger i=0; i<_fileStream.streamFragments.count; i++) {
  166. fragment = _fileStream.streamFragments[i];
  167. if (fragment.fragmentStatus) { // 已上传
  168. continue;
  169. }else { // 未上传
  170. [self->_param setObject:[NSString stringWithFormat:@"%zd",(i+1)] forKey:@"flowChunkNumber"];
  171. [self->_param setObject:@(fragment.fragmentSize) forKey:@"flowCurrentChunkSize"];
  172. dispatch_group_async(group, queue, ^{
  173. @autoreleasepool {
  174. NSData *data = [self->_fileStream readDateOfFragment:fragment];
  175. self.lastParam = self->_param;
  176. // if ([CWFileUploadManager shardUploadManager].leftStore < data.length) { // 云盘空间不足
  177. //
  178. // // 分片上传失败
  179. // self->_fileStream.fileStatus = CWUploadStatusFailed;
  180. // self->_fileStream.timeStamp = [iTools getNowTimeStamp];
  181. // [self archTaskFileStream];
  182. // // 分片上传失败通知 刷新列表
  183. // dispatch_async(dispatch_get_main_queue(), ^{
  184. // NSString *error = @"云盘剩余空间不足";
  185. // [self sendNotionWithKey:CWUploadTaskExeError userInfo:@{@"fileStream":self->_fileStream,@"error":error}];
  186. // [self sendNotionWithKey:CloudStoreIsFullNotification userInfo:@{}];
  187. // });
  188. // [self deallocSession];
  189. // return;
  190. // }
  191. HLog(@"POST之前——--文件名:%@ 已上传完进度:%.2f 上传状态:%ld", self->_fileStream.fileName, self->_fileStream.progressRate, self->_fileStream.fileStatus);
  192. [[UseAccountManage shareInstance] doUploadInfoWithParams:self->_param md5string:self->_fileStream.md5String data:data success:^(id _Nonnull responseObject) {
  193. self->_taskRepeatNum = 0;
  194. fragment.fragmentStatus = YES;
  195. [self archTaskFileStream];
  196. self->_chunkNo = i+1;
  197. dispatch_async(dispatch_get_main_queue(), ^{
  198. if (self->_fileStream.streamFragments.count == self->_chunkNo) {
  199. // 分片上传成功 是最后一片 上传完
  200. self->_fileStream.fileStatus = CWUploadStatusFinished;
  201. self->_fileStream.progressRate = 1.00;
  202. self->_fileStream.uploadDateSize = self->_fileStream.fileSize;
  203. self->_fileStream.timeStamp = [iTools getNowTimeStamp];
  204. [self archTaskFileStream];
  205. HLog(@"POST之后——--文件名:%@ 已上传完进度:%.2f 上传状态:%ld", self->_fileStream.fileName, self->_fileStream.progressRate, self->_fileStream.fileStatus);
  206. // 上传完成通知 刷新完成列表
  207. dispatch_async(dispatch_get_main_queue(), ^{
  208. if (self->_finishBlock) self->_finishBlock(self->_fileStream,nil);
  209. [self sendNotionWithKey:CWUploadTaskExeEnd userInfo:@{@"fileStream":self->_fileStream}];
  210. });
  211. }else {
  212. // 分片上传成功 不是最后一片 上传中
  213. if (self->_fileStream.uploadDateSize > self->_chunkNo * CWStreamFragmentMaxSize) { // 上传进度异常
  214. HLog(@"上传进度异常");
  215. }else {
  216. // self->_fileStream.fileStatus = CWUploadStatusUpdownloading;
  217. self->_fileStream.progressRate = self->_chunkNo * 1.00 / self->_fileStream.streamFragments.count;
  218. self->_fileStream.uploadDateSize = self->_chunkNo * CWStreamFragmentMaxSize;
  219. self->_fileStream.timeStamp = [iTools getNowTimeStamp];
  220. [self archTaskFileStream];
  221. HLog(@"POST之后——--文件名:%@ 已上传完进度:%.2f 上传状态:%ld", self->_fileStream.fileName, self->_fileStream.progressRate, self->_fileStream.fileStatus);
  222. // 上传中通知 刷新上传进度
  223. dispatch_async(dispatch_get_main_queue(), ^{
  224. if (self->_finishBlock) self->_finishBlock(self->_fileStream,nil);
  225. [self sendNotionWithKey:CWUploadTaskExeing userInfo:@{@"fileStream":self->_fileStream,@"lastParam":self->_lastParam,@"indexNo":@(self->_chunkNo)}];
  226. });
  227. }
  228. /// 继续GET
  229. [self getFileInfo];
  230. }
  231. [self deallocSession];
  232. });
  233. dispatch_semaphore_signal(semaphore);
  234. } faild:^(NSError * _Nonnull error) {
  235. if (self->_taskRepeatNum<REPEAT_MAX) { // 分片上传失败 重试3次
  236. self->_taskRepeatNum++;
  237. [self startExe];
  238. }else{
  239. // 分片上传失败
  240. self->_fileStream.fileStatus = CWUploadStatusFailed;
  241. // self->_fileStream.progressRate = (self->_chunkNo-1) / self->_fileStream.streamFragments.count;
  242. // self->_fileStream.uploadDateSize = (self->_chunkNo-1) * CWStreamFragmentMaxSize;
  243. self->_fileStream.timeStamp = [iTools getNowTimeStamp];
  244. [self archTaskFileStream];
  245. HLog(@"POST之后——--文件名:%@ 已上传完进度:%.2f 上传状态:%ld", self->_fileStream.fileName, self->_fileStream.progressRate, self->_fileStream.fileStatus);
  246. // 分片上传失败通知 刷新列表
  247. dispatch_async(dispatch_get_main_queue(), ^{
  248. if (self->_finishBlock) self->_finishBlock(self->_fileStream,error);
  249. [self sendNotionWithKey:CWUploadTaskExeError userInfo:@{@"fileStream":self->_fileStream,@"error":error}];
  250. });
  251. [self deallocSession];
  252. return;
  253. }
  254. }];
  255. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  256. }
  257. });
  258. break;
  259. }
  260. }
  261. dispatch_group_notify(group, queue, ^{
  262. if (self->_fileStream.progressRate != 1) {
  263. HLog(@"dispatch_group_notify: %.2f", self->_fileStream.progressRate);
  264. return;
  265. }
  266. self->_fileStream.fileStatus = CWUploadStatusFinished;
  267. self->_fileStream.timeStamp = [iTools getNowTimeStamp];
  268. [self archTaskFileStream];
  269. dispatch_async(dispatch_get_main_queue(), ^{
  270. if (self->_finishBlock) self->_finishBlock(self->_fileStream,nil);
  271. [self sendNotionWithKey:CWUploadTaskExeEnd userInfo:@{@"fileStream":self->_fileStream}];
  272. });
  273. [self deallocSession];
  274. });
  275. */
  276. }
  277. - (void)taskResume{
  278. _isSuspendedState = NO;
  279. if (self.uploadManager.uploadingTasks.allValues.count >= self.uploadManager.uploadMaxNum) { // 超过最大限制数量3个
  280. self.fileStream.fileStatus = CWUploadStatusWaiting;
  281. [self archTaskFileStream];
  282. HLog(@"状态修改——--文件名:%@ 已上传完进度:%.2f 上传状态:%ld", self.fileStream.fileName, self.fileStream.progressRate, self.fileStream.fileStatus);
  283. dispatch_async(dispatch_get_main_queue(), ^{
  284. if (self->_successBlock) self->_successBlock(self->_fileStream);
  285. [self sendNotionWithKey:CWUploadTaskExeSuspend userInfo:@{@"fileStream":self->_fileStream}];
  286. });
  287. return;
  288. }else {
  289. self.fileStream.fileStatus = CWUploadStatusUpdownloading;
  290. [self archTaskFileStream];
  291. HLog(@"状态修改——--文件名:%@ 已上传完进度:%.2f 上传状态:%ld", self.fileStream.fileName, self.fileStream.progressRate, self.fileStream.fileStatus);
  292. dispatch_async(dispatch_get_main_queue(), ^{
  293. if (self->_successBlock) self->_successBlock(self->_fileStream);
  294. [self sendNotionWithKey:CWUploadTaskExeing userInfo:@{@"fileStream":self->_fileStream}];
  295. });
  296. [self getFileInfo]; // 启动上传任务 先get 后post
  297. }
  298. }
  299. - (void)taskCancel{
  300. _fileStream.fileStatus = CWUploadStatusPaused;
  301. [self archTaskFileStream];
  302. _isSuspendedState = YES;
  303. dispatch_async(dispatch_get_main_queue(), ^{
  304. if (self->_finishBlock) self->_finishBlock(self->_fileStream,nil);
  305. [self sendNotionWithKey:CWUploadTaskExeSuspend userInfo:@{@"fileStream":self->_fileStream}];
  306. });
  307. }
  308. - (void)deallocSession{
  309. _taskRepeatNum = 0;
  310. }
  311. #pragma mark -- tools
  312. - (void)archTaskFileStream{
  313. NSArray *fileArray = [self->_fileStream.filePath pathComponents];
  314. NSString *filePathAccount = @"0";
  315. if (fileArray.count > 3) {
  316. filePathAccount = fileArray[fileArray.count - 3];
  317. }
  318. NSArray *plistArray = [[CWFileUploadManager shardUploadManager].plistPath pathComponents];
  319. NSString *plistPathAccount = @"1";
  320. if (plistArray.count > 3) {
  321. plistPathAccount = plistArray[plistArray.count - 3];
  322. }
  323. // HLog(@"文件缓存账号:%@ 与 plist文件账号:%@", filePathAccount, plistPathAccount);
  324. if (![filePathAccount isEqualToString:plistPathAccount]) {
  325. // HLog(@"切换账号 文件缓存账号%@ 与 plist文件账号:%@ 不一致", filePathAccount, plistPathAccount);
  326. return;
  327. }
  328. NSMutableDictionary *fsDic = [CWUploadTask unArcherThePlist:[CWFileUploadManager shardUploadManager].plistPath];
  329. if (!fsDic) {
  330. fsDic = [NSMutableDictionary dictionary];
  331. }
  332. [fsDic setObject:_fileStream forKey:_fileStream.fileName];
  333. [CWUploadTask archerTheDictionary:fsDic file:[CWFileUploadManager shardUploadManager].plistPath];
  334. }
  335. //归档
  336. + (void)archerTheDictionary:(NSDictionary *)dict file:(NSString *)path{
  337. NSData *data = [NSKeyedArchiver archivedDataWithRootObject:dict];
  338. BOOL finish = [data writeToFile:path atomically:YES];
  339. if (finish) {};
  340. }
  341. //解档
  342. + (NSMutableDictionary *)unArcherThePlist:(NSString *)path{
  343. NSMutableDictionary *dic = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
  344. return dic;
  345. }
  346. - (void)sendNotionWithKey:(NSString *)key userInfo:(NSDictionary *)dict{
  347. //创建通知
  348. NSNotification *notification =[NSNotification notificationWithName:key object:nil userInfo:dict];
  349. //通过通知中心发送通知
  350. [[NSNotificationCenter defaultCenter] postNotification:notification];
  351. }
  352. @end