nasMixUploadManager.m 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034
  1. //
  2. // nasMixUploadManager.m
  3. // 双子星云手机
  4. //
  5. // Created by xd h on 2024/8/27.
  6. //
  7. #import "nasMixUploadManager.h"
  8. #import "customUploadOperation.h"
  9. #import "nasUploadFileManager.h"
  10. #import "frpFileExistModel.h"
  11. #import "frpUploadModel.h"
  12. #define Kboundary2 @"Boundaryhxd"
  13. @interface nasMixUploadManager ()<NSURLSessionDataDelegate>
  14. //排队等候下载的上传地址数组
  15. @property(nonatomic,strong) NSMutableArray *uploadWaitingUrlArr;
  16. //正在下载的上传地址数组
  17. @property(nonatomic,strong) NSMutableArray *uploadingOperationArr;
  18. @end
  19. @implementation nasMixUploadManager
  20. + (instancetype)shareManager {
  21. static nasMixUploadManager *_instance;
  22. static dispatch_once_t onceToken;
  23. dispatch_once(&onceToken, ^{
  24. _instance = [[self alloc] init];
  25. });
  26. return _instance;
  27. }
  28. - (instancetype)init {
  29. if (self = [super init]) {
  30. _maxUploadLoadCount = 2;
  31. //[self registeNotification];
  32. }
  33. return self;
  34. }
  35. /** 添加要上传的 模型 */
  36. - (void)addUploadWithModels:(NSArray *)fileModels{
  37. HLog(@"添加任务-- %ld",fileModels.count)
  38. for (uploadFileDataModel *model in fileModels) {
  39. BOOL needAddType = YES;
  40. //1. 排查上传中
  41. for (customUploadOperation *operationDoing in self.uploadingOperationArr) {
  42. if(operationDoing.fileModel.localIdentifier && [operationDoing.fileModel.localIdentifier isEqualToString:model.localIdentifier]){
  43. needAddType = NO;
  44. break;
  45. }
  46. //文件APP上传处理
  47. if (model.curUploadFileType == uploadFileTypeFileAPP
  48. && operationDoing.fileModel.curUploadFileType == uploadFileTypeFileAPP
  49. && [operationDoing.fileModel.filename isEqualToString:model.filename]) {
  50. needAddType = NO;
  51. break;
  52. }
  53. }
  54. //1. 排查等待下载
  55. for (uploadFileDataModel *waitModel in self.uploadWaitingUrlArr) {
  56. if(waitModel.localIdentifier && [waitModel.localIdentifier isEqualToString:model.localIdentifier]){
  57. needAddType = NO;
  58. break;
  59. }
  60. //文件APP上传处理
  61. if (waitModel.curUploadFileType == uploadFileTypeFileAPP
  62. && model.curUploadFileType == uploadFileTypeFileAPP
  63. && [waitModel.filename isEqualToString:model.filename]) {
  64. needAddType = NO;
  65. break;
  66. }
  67. }
  68. if(needAddType){
  69. [self.uploadWaitingUrlArr addObject:model];
  70. }
  71. }
  72. //启动上传
  73. [self beginUploadAfterDeleteOperationBy:nil];
  74. }
  75. //在添加XX后 启动下载
  76. - (void)beginUploadAfterDeleteOperationBy:(customUploadOperation*)operation
  77. {
  78. HLog(@"beginUploadAfterDeleteOperationBy")
  79. BOOL isCanUseCellular = [HWDataManager getBoolWithKey:stringKeyAddSn(Const_file_Transfe_canUse_Cellular_all)];
  80. if(!isCanUseCellular){//不允许流量上传
  81. //
  82. if([AFNetworkReachabilityManager sharedManager].networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN){
  83. mainBlock(^{
  84. //[[NSNotificationCenter defaultCenter] postNotificationName:uploadFileSuspendAllNotification object:nil];
  85. [[iToast makeText:NSLocalizedString(@"File_Transfer_By_Cellular_tip",nil)] show];
  86. });
  87. return;
  88. }
  89. }
  90. if(ksharedAppDelegate.DisabledFileTransferType){
  91. if(ksharedAppDelegate.isImageNewFor130){
  92. [[iToast makeText:NSLocalizedString(@"File_Transfer_Disable_tip",nil)] show];
  93. }
  94. else{
  95. [[iToast makeText:NSLocalizedString(@"File_Transfer_Disable_tip2",nil)] show];
  96. }
  97. return;
  98. }
  99. if(operation){
  100. HLog(@"删除完成的上传任务")
  101. // if([operation.fileModel.filename isEqualToString:@"IMG_8876.PNG"]){
  102. // HLog(@"uploadWaitingUrlArr -- count:%ld",self.uploadWaitingUrlArr.count)
  103. // for(uploadFileDataModel*model in self.uploadWaitingUrlArr){
  104. // HLog(@"fileName--%@--%ld",model.filename,model.totalBytes)
  105. // }
  106. // }
  107. @synchronized (self) {
  108. if(operation.fileHandle){
  109. [operation closeFileHandleFun];
  110. }
  111. [self.uploadingOperationArr removeObject:operation];
  112. }
  113. }
  114. if(self.uploadingOperationArr.count == _maxUploadLoadCount){
  115. HLog(@"正在上传的数量达到了最大值 %ld",_maxUploadLoadCount)
  116. return;
  117. }
  118. if(self.uploadWaitingUrlArr.count == 0){
  119. HLog(@"没有等待中的上传任务")
  120. return;
  121. }
  122. NSInteger canAddTaskNumber = _maxUploadLoadCount - self.uploadingOperationArr.count;
  123. //新添加的任务
  124. NSMutableArray *newTaskArr = [NSMutableArray new];
  125. @synchronized (self) {
  126. for (int i=0; i<canAddTaskNumber; i++) {
  127. if(self.uploadWaitingUrlArr.count >= 1){
  128. //创建上传任务
  129. uploadFileDataModel *WaitingModel = self.uploadWaitingUrlArr.firstObject;
  130. customUploadOperation * operation = [customUploadOperation new];
  131. operation.fileModel = WaitingModel;
  132. //等待下载中的任务
  133. [self.uploadWaitingUrlArr removeObjectAtIndex:0];
  134. HLog(@"添加一个新的上传任务 %@",self.uploadingOperationArr)
  135. //添加到下载中数组
  136. [self.uploadingOperationArr addObject:operation];
  137. //[self checkFileUploadStateWithOperation:operation];
  138. [newTaskArr addObject:operation];
  139. //[weakSelf handleTaskDidUploadWith:operation withState:state];
  140. }
  141. }
  142. //开始检测新的上传任务
  143. for (customUploadOperation * operation in newTaskArr) {
  144. [self checkFileUploadStateWithOperation:operation];
  145. }
  146. [newTaskArr removeAllObjects];
  147. }
  148. }
  149. #pragma mark 检测文件是否上传过了
  150. - (void)checkFileUploadStateWithOperation:(customUploadOperation*)operation
  151. {
  152. HLog(@"%@检测文件是否过--%ld",operation.fileModel.filename,operation.fileModel.totalBytes)
  153. NSMutableDictionary*paraDict = [NSMutableDictionary new];
  154. //operation.fileModel.filename = @"Img_4107.png";// test same name file
  155. if(operation.fileModel.savePath){
  156. NSString *absPath = [[NSString alloc] initWithFormat:@"%@%@",operation.fileModel.savePath,operation.fileModel.filename];
  157. [paraDict setValue:absPath forKey:@"absPath"];
  158. NSNumber *totalBytesNumber = [NSNumber numberWithLong:operation.fileModel.totalBytes];
  159. [paraDict setValue:totalBytesNumber forKey:@"fileSize"];
  160. }
  161. KWeakSelf
  162. [[netWorkManager shareInstance] cloudPhoneGETCallBackCode:@"isFileExist" Parameters:paraDict success:^(id _Nonnull responseObject) {
  163. frpFileExistModel *model = [[frpFileExistModel alloc] initWithDictionary:responseObject error:nil];
  164. if(model && model.status == 0){
  165. [weakSelf checkFileUploadStateFunAfterNetWith:model WithOperation:operation];
  166. }
  167. else if(model && model.status == 5){//云机空间不足
  168. //上传路径
  169. NSString * backupsDefaultPath = [HWDataManager getStringWithKey:stringKeyAddSn(Const_photo_upload_default_path)];
  170. NSArray * stringArr = [backupsDefaultPath componentsSeparatedByString:@"/"];
  171. NSString *lastStr = stringArr.lastObject;
  172. NSString *repStr = [[NSString alloc] initWithFormat:@"/%@",lastStr];
  173. NSString *name = [backupsDefaultPath stringByReplacingOccurrencesOfString:repStr withString:@""];
  174. name = [iTools changePathToShowPathBy:name];
  175. NSString* showTip = [[NSString alloc] initWithFormat:@"【%@】%@",name,NSLocalizedString(@"disk_space_not_tip_for_95",nil)];
  176. [[iToast makeText:showTip] show];
  177. [weakSelf handleUploadFailOneFileBy:operation];
  178. }
  179. else{
  180. HLog(@"isFileExist接口异常:%@",responseObject)
  181. [weakSelf handleUploadFailOneFileBy:operation];
  182. }
  183. } failure:^(NSError * _Nonnull error) {
  184. HLog(@"%@",error)
  185. [weakSelf handleUploadFailOneFileBy:operation];
  186. }];
  187. }
  188. #pragma mark 检测分家是否存在后处理 是否要上传
  189. - (void)checkFileUploadStateFunAfterNetWith:(frpFileExistModel*)model WithOperation:(customUploadOperation*)operation
  190. {
  191. if(!model){
  192. [self handleUploadFailOneFileBy:operation];
  193. return;
  194. }
  195. operation.fileModel.didUploadBytes = 0;
  196. operation.fileModel.taskId = model.data.fileTaskId;
  197. if(!model.data.exist){//未上传过
  198. HLog(@"%@检测到文件未上传过--%ld",operation.fileModel.filename,operation.fileModel.totalBytes)
  199. [self prepareToUploadFileWithOperation:operation];
  200. }
  201. else if(model.data.isComplete){//上传过了 并且文件上传完了
  202. //判断下文件创建长度是否一致 一致则是上传完了 不一致 重新上传一个 可能是同名的文件而已
  203. // if(model.data.size >= operation.fileModel.totalBytes){//上传完了
  204. // [self handleFileDidUploadFunWithOperation:operation];
  205. // }
  206. // else{//未上传完 isComplete 这个字段有问题
  207. // operation.fileModel.didUploadBytes = model.data.size;
  208. // [self prepareToUploadFileWithOperation:operation];
  209. // }
  210. HLog(@"%@检测到文件上传完成--%ld",operation.fileModel.filename,operation.fileModel.totalBytes)
  211. //修复同名文件丢失问题
  212. if(model.data.size == operation.fileModel.totalBytes){//上传完了
  213. HLog(@"1111111")
  214. [self handleFileDidUploadFunWithOperation:operation];
  215. }
  216. else{//同名文件 处理
  217. HLog(@"2222222")
  218. [self prepareToUploadFileWithOperation:operation];
  219. }
  220. }
  221. else{//上传过了 未上传完成
  222. HLog(@"%@检测到文件上传过了 未上传完成--%ld",operation.fileModel.filename,operation.fileModel.totalBytes)
  223. operation.fileModel.didUploadBytes = model.data.size;
  224. [self prepareToUploadFileWithOperation:operation];
  225. }
  226. }
  227. #pragma mark 任务上传过了了
  228. - (void)handleFileDidUploadFunWithOperation:(customUploadOperation*)operation
  229. {
  230. // mainBlock(^{
  231. // [[iToast makeText:NSLocalizedString(@"File_upload_file_already_exists",nil)] show];
  232. // });
  233. //
  234. // NSMutableArray *delArr = [NSMutableArray new];
  235. // [delArr addObject:operation.fileModel];
  236. //
  237. // [[nasUploadFileManager shareInstance] deleteUploadFileRecordBy:delArr withDelCache:YES complete:^(BOOL isSuccess) {
  238. // if (isSuccess) {
  239. //
  240. // }
  241. // }];
  242. //改为走上传完成的逻辑
  243. [self handleUploadDoneOneFileBy:operation];
  244. }
  245. //加个接口 做删除用的
  246. - (void)deleteUploadFileRecordWithOperation:(customUploadOperation*)operation{
  247. NSMutableArray *delArr = [NSMutableArray new];
  248. [delArr addObject:operation.fileModel];
  249. [[nasUploadFileManager shareInstance] deleteUploadFileRecordBy:delArr withDelCache:YES complete:^(BOOL isSuccess) {
  250. if (isSuccess) {
  251. }
  252. }];
  253. }
  254. #pragma mark 准备上传文件
  255. - (void)prepareToUploadFileWithOperation:(customUploadOperation*)operation
  256. {
  257. NSMutableDictionary *paraDict = [NSMutableDictionary new];
  258. NSString* taskUid = operation.fileModel.taskId;
  259. if(!taskUid || taskUid.length == 0){
  260. taskUid = [iTools getTaskUidStr];
  261. operation.fileModel.taskId = taskUid;
  262. }
  263. [paraDict setObject:taskUid forKey:@"taskId"];
  264. [paraDict setObject:@0 forKey:@"position"];
  265. [paraDict setObject:@"true" forKey:@"isLast"];
  266. if(operation.fileModel.savePath){
  267. [paraDict setObject:operation.fileModel.savePath forKey:@"savePath"];
  268. }
  269. else{
  270. HLog(@"获取保存路径失败")
  271. [self handleUploadFailOneFileBy:operation];
  272. return;
  273. }
  274. if(operation.fileModel.filename){
  275. [paraDict setObject:operation.fileModel.filename forKey:@"filename"];
  276. }
  277. else{
  278. HLog(@"获取用户名失败")
  279. return;
  280. }
  281. KWeakSelf
  282. if(operation.fileModel.curUploadFileType == uploadFileTypeImage){
  283. [paraDict setObject:@1 forKey:@"imageType"];
  284. if(!operation.fileModel.imageData){
  285. NSString*pathStr = [cachesFileManager getFilePathWithName:operation.fileModel.filename type:operation.fileModel.curUploadFileType];
  286. NSData *imageData = [NSData dataWithContentsOfFile:pathStr];
  287. //修复出现过 图片数据小于totalBytes
  288. if(imageData){
  289. long curDataLengt = [imageData length];
  290. if(curDataLengt == operation.fileModel.didUploadBytes && curDataLengt < operation.fileModel.totalBytes){
  291. HLog(@"totalBytes 获取出错?")
  292. [self deleteUploadFileRecordWithOperation:operation];
  293. return;
  294. }
  295. }
  296. if (!imageData) {
  297. if(!operation.fileModel.asset){
  298. NSString *curLocalIdentifier = operation.fileModel.localIdentifier;
  299. PHFetchResult *fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[curLocalIdentifier] options:nil];
  300. PHAsset *asset = fetchResult.firstObject;
  301. operation.fileModel.asset = asset;
  302. }
  303. PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
  304. options.networkAccessAllowed = YES; // Allow downloading from iCloud
  305. options.version = PHImageRequestOptionsVersionCurrent;
  306. options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
  307. [[PHImageManager defaultManager] requestImageDataForAsset:operation.fileModel.asset options:options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
  308. // 直接得到最终的 NSData 数据
  309. if (imageData) {
  310. operation.fileModel.imageData = imageData;
  311. if(operation.fileModel.totalBytes == 0){
  312. operation.fileModel.totalBytes = [imageData length];
  313. }
  314. [weakSelf afterGetImageDataFunWithOperation:operation];
  315. }
  316. else{
  317. HLog(@"\n\n\n\nrequestImageDataForAsset fail imageData nil\n\n\n")
  318. [weakSelf handleUploadFailOneFileBy:operation];
  319. }
  320. }];
  321. return;
  322. }
  323. operation.fileModel.imageData = imageData;
  324. }
  325. HLog(@"%@开始上传--%ld",operation.fileModel.filename,operation.fileModel.totalBytes)
  326. NSData *curData = operation.fileModel.imageData;
  327. operation.onceDataLengt = [curData length];
  328. if(operation.fileModel.totalBytes == 0){//异常处理
  329. operation.fileModel.totalBytes = operation.onceDataLengt;
  330. }
  331. [self startUpload:paraDict operation:operation data:curData success:^(id _Nonnull responseObject) {
  332. frpUploadModel *model = [[frpUploadModel alloc] initWithDictionary:responseObject error:nil];
  333. if(model && model.position == operation.fileModel.totalBytes && model.position != 0){
  334. HLog(@"%@上传完成 000---%ld",operation.fileModel.filename,operation.fileModel.totalBytes)
  335. [weakSelf handleUploadDoneOneFileBy:operation];
  336. }
  337. else{
  338. HLog(@"%@上传完成异常 %ld---%ld",operation.fileModel.filename,model.position,operation.fileModel.totalBytes)
  339. [weakSelf handleUploadDoneOneFileBy:operation];
  340. }
  341. } faild:^(NSError * _Nonnull error) {
  342. HLog(@"%@上传失败",operation.fileModel.filename)
  343. [weakSelf handleUploadFailOneFileBy:operation];
  344. }];
  345. }
  346. else if(operation.fileModel.curUploadFileType == uploadFileTypeVideo){
  347. [paraDict setObject:@1 forKey:@"videoType"];
  348. [paraDict setObject:@"false" forKey:@"isLast"];
  349. if(![cachesFileManager checkFileIsSaveState:operation.fileModel.filename withType:uploadFileTypeVideo]){
  350. if(!operation.fileModel.asset){
  351. NSString *curLocalIdentifier = operation.fileModel.localIdentifier;
  352. PHFetchResult *fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[curLocalIdentifier] options:nil];
  353. PHAsset *asset = fetchResult.firstObject;
  354. operation.fileModel.asset = asset;
  355. }
  356. //真正的视频数据
  357. PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
  358. options.version = PHVideoRequestOptionsVersionOriginal;
  359. options.networkAccessAllowed = YES; // Allow downloading from iCloud
  360. [[PHImageManager defaultManager] requestAVAssetForVideo:operation.fileModel.asset options:options resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
  361. if ([asset isKindOfClass:[AVURLAsset class]]) {
  362. AVURLAsset* urlAsset = (AVURLAsset*)asset;
  363. BOOL isSuc = [cachesFileManager copyVideoItemAtPath:[urlAsset.URL path] fileName:operation.fileModel.filename error:nil];
  364. if (isSuc) {
  365. [weakSelf afterGetVideoDataFunWithOperation:operation];
  366. }
  367. else{
  368. [weakSelf handleUploadFailOneFileBy:operation];
  369. }
  370. }
  371. else{
  372. [weakSelf handleUploadFailOneFileBy:operation];
  373. }
  374. }];
  375. return;
  376. }
  377. long curPosition = operation.fileModel.didUploadBytes;
  378. [self beginUploadVideoDataFunBy:operation with:curPosition withPara:paraDict success:^(id _Nonnull responseObject) {
  379. } faild:^(NSError * _Nonnull error) {
  380. }];
  381. }
  382. else if(operation.fileModel.curUploadFileType == uploadFileTypeFileAPP){
  383. //上传文件
  384. [paraDict setObject:@3 forKey:@"videoType"];
  385. [paraDict setObject:@"false" forKey:@"isLast"];
  386. if(![cachesFileManager checkFileIsSaveState:operation.fileModel.filename withType:uploadFileTypeFileAPP]){
  387. [weakSelf handleUploadFailOneFileBy:operation];
  388. return;
  389. }
  390. long curPosition = operation.fileModel.didUploadBytes;
  391. [self beginUploadVideoDataFunBy:operation with:curPosition withPara:paraDict success:^(id _Nonnull responseObject) {
  392. } faild:^(NSError * _Nonnull error) {
  393. }];
  394. }
  395. }
  396. #pragma mark 视频上传 或者文件APP上传
  397. - (void)beginUploadVideoDataFunBy:(customUploadOperation*)operation with:(NSInteger)position withPara:(NSMutableDictionary*)paraDict success:(netWork_Success)success faild:(netWork_Faild)faildStr
  398. {
  399. if(operation.isCancelType){
  400. HLog(@"防止取消不了任务生效")
  401. return;
  402. }
  403. BOOL isLastPicece = NO;
  404. if((operation.fileModel.totalBytes - position) <= MaxNasUploadPieceSzie){
  405. [paraDict setObject:@"true" forKey:@"isLast"];
  406. isLastPicece = YES;
  407. }
  408. else{
  409. [paraDict setObject:@"false" forKey:@"isLast"];
  410. }
  411. [paraDict setObject:[NSNumber numberWithLong:position] forKey:@"position"];
  412. //视频数据切片
  413. __block NSData *videoData = [self cutVideoFileFunAtIndex:position withMaxLenght:MaxNasUploadPieceSzie withModel:operation.fileModel withOperation:operation];
  414. if(!videoData ||videoData.length ==0){
  415. HLog(@"视频没获取到")
  416. [self handleUploadFailOneFileBy:operation];
  417. return;
  418. }
  419. operation.onceDataLengt = [videoData length];
  420. KWeakSelf
  421. [self startUpload:paraDict operation:operation data:videoData success:^(id _Nonnull responseObject) {
  422. frpUploadModel *model = [[frpUploadModel alloc] initWithDictionary:responseObject error:nil];
  423. if(model && model.position >= operation.fileModel.totalBytes){
  424. HLog(@"%@上传完成 001",operation.fileModel.filename)
  425. [weakSelf handleUploadDoneOneFileBy:operation];
  426. }
  427. else{
  428. HLog(@"%@上传完成一片 %ld",operation.fileModel.filename,model.position)
  429. [weakSelf beginUploadVideoDataFunBy:operation with:model.position withPara:paraDict success:^(id _Nonnull responseObject) {
  430. success(responseObject);
  431. } faild:^(NSError * _Nonnull error) {
  432. NSError *err = error;
  433. if(error.code != -999){
  434. faildStr(err);
  435. }
  436. }];
  437. }
  438. } faild:^(NSError * _Nonnull error) {
  439. HLog(@"%@上传失败",operation.fileModel.filename)
  440. [weakSelf handleUploadFailOneFileBy:operation];
  441. }];
  442. }
  443. #pragma mark 分段读视频文件
  444. -(NSData*)cutVideoFileFunAtIndex:(NSUInteger)dataIndex withMaxLenght:(NSInteger)maxLengt withModel:(uploadFileDataModel*)dataModel withOperation:(customUploadOperation*)operation{
  445. //return [operation cutVideoFileFunAtIndex:dataIndex withMaxLenght:maxLengt];
  446. HLog(@"视频切片开始 线程:%@",[NSThread currentThread]);
  447. //视频
  448. NSString *filePath = [cachesFileManager getFilePathWithName:dataModel.filename type:uploadFileTypeVideo]; // 文件路径
  449. //文件APP
  450. if (dataModel.curUploadFileType == uploadFileTypeFileAPP) {
  451. filePath = [cachesFileManager getFilePathWithName:dataModel.filename type:uploadFileTypeFileAPP]; // 文件路径
  452. }
  453. NSFileManager *manager0 = [NSFileManager defaultManager];
  454. if(![manager0 fileExistsAtPath:filePath]) {
  455. return [NSData new];
  456. }
  457. NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath]; // 创建文件句柄
  458. // 设置分段读取的大小,这里以每次读取1KB为例
  459. const NSUInteger chunkSize = maxLengt;//cutVideoPieceSzie;//5 * 1024 *1024;
  460. NSMutableData *data = [NSMutableData data];
  461. if (fileHandle) {
  462. long long endOfFile = [fileHandle seekToEndOfFile];
  463. if(dataModel.totalBytes == 0
  464. || dataModel.totalBytes < endOfFile){//异常处理
  465. dataModel.totalBytes = endOfFile;
  466. }
  467. //异常处理
  468. if(endOfFile == dataIndex){
  469. dataModel.totalBytes = endOfFile;
  470. dataModel.didUploadBytes = endOfFile;
  471. dataModel.curUploadStateType = uploadStateDone;
  472. [fileHandle closeFile];
  473. return data;
  474. }
  475. if (endOfFile >= chunkSize) {
  476. // 读取文件的分段数据到某个位置
  477. [fileHandle seekToFileOffset:dataIndex];
  478. // 读取文件的分段数据
  479. NSData* chunk = [fileHandle readDataOfLength:chunkSize];
  480. if (chunk) {
  481. [data appendData:chunk];
  482. }
  483. }
  484. else{
  485. // 读取文件的分段数据到某个位置
  486. [fileHandle seekToFileOffset:dataIndex];
  487. [data appendData:[fileHandle readDataToEndOfFile]];
  488. }
  489. // 在这里可以对文件内容进行处理
  490. // ...
  491. // 关闭文件句柄
  492. [fileHandle closeFile];
  493. }
  494. HLog(@"视频切片完成 dataIndex:%ld --长度:%ld",dataIndex,[data length])
  495. return data;
  496. }
  497. #pragma mark 处理上传完成
  498. - (void)handleUploadDoneOneFileBy:(customUploadOperation*)operation
  499. {
  500. HLog(@"handleUploadDoneOneFileBy")
  501. [[NSNotificationCenter defaultCenter] postNotificationName:nasUploadTaskExeEnd object:operation.fileModel];
  502. [self beginUploadAfterDeleteOperationBy:operation];
  503. }
  504. #pragma mark 处理删除失败
  505. - (void)handleUploadFailOneFileBy:(customUploadOperation*)operation
  506. {
  507. operation.fileModel.curUploadStateType = uploadStateFail;
  508. [[NSNotificationCenter defaultCenter] postNotificationName:nasUploadTaskExeError object:operation.fileModel];
  509. [self beginUploadAfterDeleteOperationBy:operation];
  510. }
  511. #pragma mark 根据 asset 获取到图片数据
  512. - (void)afterGetImageDataFunWithOperation:(customUploadOperation*)operation
  513. {
  514. [cachesFileManager getFileNameWithContent:operation.fileModel.imageData fileName:operation.fileModel.filename type:uploadFileTypeImage];
  515. [self prepareToUploadFileWithOperation:operation];
  516. }
  517. #pragma mark 根据 asset 获取到视频数据
  518. - (void)afterGetVideoDataFunWithOperation:(customUploadOperation*)operation
  519. {
  520. [self prepareToUploadFileWithOperation:operation];
  521. }
  522. #pragma mark 开始上传
  523. - (void)startUpload:(NSMutableDictionary *)params operation:(customUploadOperation*)operation data:(NSData *)data success:(netWork_Success)success faild:(netWork_Faild)faildStr {
  524. NSString *urlString = ksharedAppDelegate.NASFileByBoxService;
  525. urlString = [[NSString alloc] initWithFormat:@"%@uploadFile",urlString];
  526. NSURL *URL = [NSURL URLWithString:urlString];
  527. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
  528. [request setHTTPMethod:@"POST"];
  529. //请求体
  530. NSMutableData *bodyData = [self getBodyDataWithRequest:request withModel:operation.fileModel withData:data withPara:params];
  531. //设置请求体
  532. [request setHTTPMethod:@"POST"];
  533. [request setHTTPBody:bodyData];
  534. //设置请求体长度
  535. NSInteger length = [bodyData length];
  536. [request setValue:[NSString stringWithFormat:@"%ld",length] forHTTPHeaderField:@"Content-Length"];
  537. //设置 POST请求文件上传
  538. [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",Kboundary2] forHTTPHeaderField:@"Content-Type"];
  539. KWeakSelf
  540. //回话对象
  541. NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
  542. operation.session = session;
  543. //请求task
  544. /*
  545. 第一个参数:请求对象
  546. 第二个参数:传递是要上传的数据(请求体)
  547. 第三个参数:
  548. */
  549. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:nil completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  550. //解析
  551. [weakSelf handleCustomUploadResultBy:data withResponse:response withError:error success:success faild:faildStr];
  552. }];
  553. // NSString *filePath = [cachesFileManager getFilePathWithName:dataModel.filename type:uploadFileTypeVideo]; // 文件路径
  554. // NSURL *filePathUrl = [NSURL URLWithString:filePath];
  555. //
  556. // NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:filePathUrl completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  557. // HLog(@"data string:%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
  558. //[weakSelf handleCustomUploadResultBy:data withResponse:response withError:error];
  559. // }];
  560. // NSURLSessionDataTask *uploadTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  561. //
  562. // HLog(@"data string:%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
  563. //[weakSelf handleCustomUploadResultBy:data withResponse:response withError:error];
  564. //
  565. //// NSJSONSerialization *object = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
  566. //// NSDictionary *dict = (NSDictionary *)object;
  567. //// NSLog(@"=====%@",[dict objectForKey:@"success"]);
  568. // }];
  569. //执行Task
  570. [uploadTask resume];
  571. operation.dataTask = uploadTask;
  572. }
  573. #pragma mark 用系统方法写的上传处理
  574. - (void)handleCustomUploadResultBy:(NSData*)data withResponse:(NSURLResponse*)response withError:(NSError*)error success:(netWork_Success)success faild:(netWork_Faild)faildStr{
  575. if(error){
  576. HLog(@"上传错误:%@",error)
  577. // -1005 网络中断 1009 网络似乎中断
  578. if(error.code == -1005
  579. ||error.code == -1009){//网络中断
  580. [[nasUploadFileManager shareInstance] saveUploadingTaskByNetWorkErrorFun];
  581. return;
  582. }
  583. if(error.code != -999){
  584. faildStr(error);
  585. }
  586. return;
  587. }
  588. NSJSONSerialization *object = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
  589. NSDictionary *dict = (NSDictionary *)object;
  590. frpUploadModel *model = [[frpUploadModel alloc] initWithDictionary:dict error:nil];
  591. if(model && [model.msg isEqualToString:@"success"]){
  592. success(dict);
  593. }
  594. else{
  595. NSError *err = [NSError new];
  596. faildStr(err);
  597. }
  598. }
  599. - (NSMutableData *)getBodyDataWithRequest:(NSMutableURLRequest *)request withModel:(uploadFileDataModel*)dataModel withData:(NSData*)data withPara:(NSMutableDictionary*)params{
  600. //1 边界符号要配置请求头里面去
  601. /*
  602. multipart/form-data 是表单格式
  603. charset=utf-8 是utf-8编码
  604. bounary 是表单开头
  605. */
  606. [request setValue:[NSString stringWithFormat:@"multipart/form-data; charset=utf-8; boundary=%@", Kboundary2] forHTTPHeaderField:@"Content-Type"];
  607. /// body
  608. NSMutableData *boydData = [NSMutableData data];
  609. // 2.1 边界符号(开始边界)
  610. //2.1.1 其它参数
  611. NSMutableString *paraString = [NSMutableString new];
  612. //[paraString appendFormat:@"--%@\r\n",Kboundary];//\n:换行 \n:切换到行首
  613. for (NSString *key in params) {
  614. NSString *value = params[key];
  615. [paraString appendFormat:@"--%@\r\n",Kboundary2];//\n:换行 \n:切换到行首
  616. [paraString appendFormat:@"Content-Disposition: form-data; name=\"%@\"",key];
  617. [paraString appendFormat:@"\r\n"];
  618. [paraString appendFormat:@"\r\n"];
  619. [paraString appendFormat:@"%@\r\n",value];
  620. }
  621. //[boydData appendData:[paraString dataUsingEncoding:NSUTF8StringEncoding]];
  622. // body每一个段内容以换行符作为结束标示
  623. NSString *fileBeginBoundary = [NSString stringWithFormat:@"--%@\r\n", Kboundary2];
  624. //[boydData appendData:[fileBeginBoundary dataUsingEncoding:NSUTF8StringEncoding]];
  625. [paraString appendString:fileBeginBoundary];
  626. // 2.2 属性配置 名字;key;类型
  627. NSString *serverFileKey = @"image"; //key
  628. //NSString *serverFileKey = @"file";
  629. NSString *serverContentTypes = @"image/png"; //类型
  630. if (dataModel.curUploadFileType == uploadFileTypeVideo) {
  631. serverFileKey = @"video";
  632. serverContentTypes = @"video/mp4";
  633. }
  634. NSString *serverFileName = dataModel.filename; //name
  635. // filename已命名文件; name相当于一个key, 这个名字和服务器保持一致
  636. /*
  637. 理解key,表单发送给服务端,服务端拿到数据之后,可以将任务解析成一个字典了imageDict;图片数据会通过这个字典里面的name来获取图片(伪代码 image = imageDict[serverFileKey])
  638. */
  639. //2.3 拼接数据(创建一个字符串来拼装)
  640. NSMutableString *string = [NSMutableString new];
  641. [string appendFormat:@"Content-Disposition:form-data; name=\"%@\"; filename=\"%@\" ", @"file", serverFileName];
  642. //[string appendFormat:@"%@", KNewLine];
  643. [string appendFormat:@"\r\n"];
  644. [string appendFormat:@"Content-Type:%@", serverContentTypes];
  645. // [string appendFormat:@"%@", KNewLine];
  646. // [string appendFormat:@"%@", KNewLine];
  647. [string appendFormat:@"\r\n"];
  648. [string appendFormat:@"\r\n"];
  649. // [boydData appendData:[string dataUsingEncoding:NSUTF8StringEncoding]];
  650. [paraString appendString:string];
  651. [boydData appendData:[paraString dataUsingEncoding:NSUTF8StringEncoding]];
  652. // 2.3 拼接数据(拼接文件数据)
  653. [boydData appendData:data];
  654. // 2.4 边界符号 (结束边界)
  655. NSString *fileEndBoundary = [NSString stringWithFormat:@"\r\n--%@--", Kboundary2];
  656. [boydData appendData:[fileEndBoundary dataUsingEncoding:NSUTF8StringEncoding]];
  657. return boydData;
  658. }
  659. #pragma mark 上传的文件已存在
  660. - (void)handleTaskDidUploadWith:(customUploadOperation*)operation withState:(NSInteger)state
  661. {
  662. if(state == 1){
  663. [[iToast makeText:NSLocalizedString(@"File_upload_file_already_exists",nil)] show];
  664. }
  665. HLog(@"上传的文件已存在 删除任务:_uploadingOperationArr:%@",_uploadingOperationArr);
  666. [self.uploadingOperationArr removeObject:operation];
  667. NSMutableArray *delArr = [NSMutableArray new];
  668. [delArr addObject:operation.fileModel];
  669. [[nasUploadFileManager shareInstance] deleteUploadFileRecordBy:delArr withDelCache:NO complete:^(BOOL isSuccess) {
  670. if (isSuccess) {
  671. }
  672. }];
  673. [self beginUploadAfterDeleteOperationBy:nil];
  674. }
  675. #pragma mark - <NSURLSessionDataDelegate>
  676. // ssl 服务 证书信任
  677. - (void)URLSession:(NSURLSession *)session
  678. didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
  679. completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{
  680. if(![challenge.protectionSpace.authenticationMethod isEqualToString:@"NSURLAuthenticationMethodServerTrust"]) {
  681. return;
  682. }
  683. // 信任该插件
  684. NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust];
  685. // 第一个参数 告诉系统如何处置
  686. completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
  687. }
  688. //当请求协议是https的时候回调用该方法
  689. //Challenge 挑战 质询(受保护空间)
  690. //NSURLAuthenticationMethodServerTrust 服务器信任证书
  691. - (void)URLSession:(NSURLSession *)session
  692. task:(NSURLSessionTask *)task
  693. didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
  694. completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler {
  695. if(![challenge.protectionSpace.authenticationMethod isEqualToString:@"NSURLAuthenticationMethodServerTrust"]) {
  696. return;
  697. }
  698. // 信任该插件
  699. NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust];
  700. // 第一个参数 告诉系统如何处置
  701. completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
  702. }
  703. // 接受到响应调用
  704. - (void)URLSession:(NSURLSession *)session
  705. dataTask:(NSURLSessionDataTask *)dataTask
  706. didReceiveResponse:(NSURLResponse *)response
  707. completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
  708. completionHandler(NSURLSessionResponseAllow);
  709. }
  710. // 上传进度
  711. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
  712. //每包发送的大小bytesSent,totalBytesSent已经上传了多少;totalBytesExpectedToSend总共要发送多少
  713. // 32768 = 32KB
  714. HLog(@"didSendBodyData: %lld--%lld-%lld", bytesSent, totalBytesSent, totalBytesExpectedToSend);
  715. @synchronized (self) {
  716. for (customUploadOperation*operation in _uploadingOperationArr) {
  717. HLog(@"正在遍历数组 _uploadingOperationArr:%@",_uploadingOperationArr);
  718. if(operation.dataTask == task){
  719. operation.fileModel.didUploadBytes += bytesSent;
  720. //调整下大小 因为上传的数据 除了data 长度 还包含了 参数大小
  721. if(totalBytesSent == totalBytesExpectedToSend){
  722. operation.fileModel.didUploadBytes -= (totalBytesExpectedToSend - operation.onceDataLengt);
  723. }
  724. NSTimeInterval curTime = [[NSDate date] timeIntervalSince1970];
  725. NSTimeInterval timeDiff = curTime - operation.preNotTimeInterval;
  726. //HLog(@"控制刷新时间为1秒:%f",timeDiff);
  727. CGFloat RefreshTimer = 0.8;
  728. if(operation.fileModel.totalBytes <= 5*1024*1024){//小于5M 事实
  729. RefreshTimer = 0.2;
  730. }
  731. if (operation.preNotTimeInterval <= 0
  732. || timeDiff > RefreshTimer ) {
  733. operation.preNotTimeInterval = curTime;
  734. [[NSNotificationCenter defaultCenter] postNotificationName:uploadFileUploadingNotification object:operation.fileModel];
  735. }
  736. break;
  737. }
  738. }
  739. }
  740. }
  741. #pragma mark 取消单个任务
  742. - (void)cancelUploadTaskFunWith:(uploadFileDataModel*)fileModel
  743. {
  744. HLog(@"取消任务-- %@--%@",fileModel.filename,fileModel.taskId)
  745. @synchronized (self) {
  746. for (customUploadOperation*operation in _uploadingOperationArr) {
  747. HLog(@"正在遍历数组 _uploadingOperationArr:%@",_uploadingOperationArr);
  748. if([operation.fileModel.localIdentifier isEqualToString:fileModel.localIdentifier]){
  749. operation.isCancelType = YES;
  750. [operation.dataTask cancel];
  751. if(operation.fileHandle){
  752. [operation closeFileHandleFun];
  753. }
  754. [self beginUploadAfterDeleteOperationBy:operation];
  755. return;
  756. }
  757. }
  758. for (uploadFileDataModel*waitModel in _uploadWaitingUrlArr) {
  759. if(//[waitModel.filename isEqualToString:fileModel.filename]
  760. [waitModel.localIdentifier isEqualToString:fileModel.localIdentifier]){
  761. [_uploadWaitingUrlArr removeObject:waitModel];
  762. [self beginUploadAfterDeleteOperationBy:nil];
  763. return;
  764. }
  765. }
  766. }
  767. }
  768. #pragma mark 取消所有任务
  769. - (void)cancelUploadAllTaskFun
  770. {
  771. HLog(@"取消所有任务")
  772. @synchronized (self) {
  773. [_uploadWaitingUrlArr removeAllObjects];
  774. for (customUploadOperation*operation in _uploadingOperationArr) {
  775. HLog(@"正在遍历数组 _uploadingOperationArr:%@",_uploadingOperationArr);
  776. operation.isCancelType = YES;
  777. [operation.dataTask cancel];
  778. if(operation.fileHandle){
  779. [operation closeFileHandleFun];
  780. }
  781. }
  782. [_uploadingOperationArr removeAllObjects];
  783. }
  784. }
  785. #pragma mark 判断是否在上传中
  786. - (BOOL)checkUploadTaskDoingFun
  787. {
  788. if(_uploadWaitingUrlArr.count >0 || _uploadingOperationArr.count>0){
  789. return YES;
  790. }
  791. return NO;
  792. }
  793. - (BOOL)isUploadIngType
  794. {
  795. if(self.uploadWaitingUrlArr.count >0){
  796. return YES;
  797. }
  798. if(self.uploadingOperationArr.count >0){
  799. return YES;
  800. }
  801. return NO;
  802. }
  803. #pragma mark - lazy load
  804. - (NSMutableArray *)uploadWaitingUrlArr {
  805. if (!_uploadWaitingUrlArr) {
  806. _uploadWaitingUrlArr = [NSMutableArray array];
  807. }
  808. return _uploadWaitingUrlArr;
  809. }
  810. - (NSMutableArray *)uploadingOperationArr {
  811. if (!_uploadingOperationArr) {
  812. _uploadingOperationArr = [NSMutableArray array];
  813. }
  814. return _uploadingOperationArr;
  815. }
  816. @end