// // nasMixUploadManager.m // Private-X // // Created by xd h on 2024/8/27. // #import "nasMixUploadManager.h" #import "customUploadOperation.h" #import "nasUploadFileManager.h" #import "frpFileExistModel.h" #import "frpUploadModel.h" #define Kboundary2 @"Boundaryhxd" @interface nasMixUploadManager () //排队等候下载的上传地址数组 @property(nonatomic,strong) NSMutableArray *uploadWaitingUrlArr; //正在下载的上传地址数组 @property(nonatomic,strong) NSMutableArray *uploadingOperationArr; @end @implementation nasMixUploadManager + (instancetype)shareManager { static nasMixUploadManager *_instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [[self alloc] init]; }); return _instance; } - (instancetype)init { if (self = [super init]) { _maxUploadLoadCount = 2; //[self registeNotification]; } return self; } /** 添加要上传的 模型 */ - (void)addUploadWithModels:(NSArray *)fileModels{ HLog(@"添加任务-- %ld",fileModels.count) for (uploadFileDataModel *model in fileModels) { BOOL needAddType = YES; //1. 排查上传中 for (customUploadOperation *operationDoing in self.uploadingOperationArr) { if(operationDoing.fileModel.localIdentifier && [operationDoing.fileModel.localIdentifier isEqualToString:model.localIdentifier]){ needAddType = NO; break; } //文件APP上传处理 if (model.curUploadFileType == uploadFileTypeFileAPP && operationDoing.fileModel.curUploadFileType == uploadFileTypeFileAPP && [operationDoing.fileModel.filename isEqualToString:model.filename]) { needAddType = NO; break; } //录音上传处理 if (model.curUploadFileType == uploadFileTypeRecord && operationDoing.fileModel.curUploadFileType == uploadFileTypeRecord && [operationDoing.fileModel.filename isEqualToString:model.filename]) { needAddType = NO; break; } } //1. 排查等待下载 for (uploadFileDataModel *waitModel in self.uploadWaitingUrlArr) { if(waitModel.localIdentifier && [waitModel.localIdentifier isEqualToString:model.localIdentifier]){ needAddType = NO; break; } //文件APP上传处理 if (waitModel.curUploadFileType == uploadFileTypeFileAPP && model.curUploadFileType == uploadFileTypeFileAPP && [waitModel.filename isEqualToString:model.filename]) { needAddType = NO; break; } //录音上传处理 if (waitModel.curUploadFileType == uploadFileTypeRecord && model.curUploadFileType == uploadFileTypeRecord && [waitModel.filename isEqualToString:model.filename]) { needAddType = NO; break; } } if(needAddType){ [self.uploadWaitingUrlArr addObject:model]; } } //启动上传 [self beginUploadAfterDeleteOperationBy:nil]; } //在添加XX后 启动下载 - (void)beginUploadAfterDeleteOperationBy:(customUploadOperation*)operation { HLog(@"beginUploadAfterDeleteOperationBy") BOOL isCanUseCellular = [HWDataManager getBoolWithKey:stringKeyAddSn(Const_file_Transfe_canUse_Cellular_all)]; if(!isCanUseCellular){//不允许流量上传 // if([AFNetworkReachabilityManager sharedManager].networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN){ mainBlock(^{ //[[NSNotificationCenter defaultCenter] postNotificationName:uploadFileSuspendAllNotification object:nil]; [[iToast makeText:NSLocalizedString(@"File_Transfer_By_Cellular_tip",nil)] show]; }); return; } } if(ksharedAppDelegate.DisabledFileTransferType){ if(ksharedAppDelegate.isImageNewFor130){ [[iToast makeText:NSLocalizedString(@"File_Transfer_Disable_tip",nil)] show]; } else{ [[iToast makeText:NSLocalizedString(@"File_Transfer_Disable_tip2",nil)] show]; } return; } if(operation){ HLog(@"删除完成的上传任务") // if([operation.fileModel.filename isEqualToString:@"IMG_8876.PNG"]){ // HLog(@"uploadWaitingUrlArr -- count:%ld",self.uploadWaitingUrlArr.count) // for(uploadFileDataModel*model in self.uploadWaitingUrlArr){ // HLog(@"fileName--%@--%ld",model.filename,model.totalBytes) // } // } @synchronized (self) { if(operation.fileHandle){ [operation closeFileHandleFun]; } [self.uploadingOperationArr removeObject:operation]; } } if(self.uploadingOperationArr.count == _maxUploadLoadCount){ HLog(@"正在上传的数量达到了最大值 %ld",_maxUploadLoadCount) return; } if(self.uploadWaitingUrlArr.count == 0){ HLog(@"没有等待中的上传任务") return; } NSInteger canAddTaskNumber = _maxUploadLoadCount - self.uploadingOperationArr.count; //新添加的任务 NSMutableArray *newTaskArr = [NSMutableArray new]; @synchronized (self) { for (int i=0; i= 1){ //创建上传任务 uploadFileDataModel *WaitingModel = self.uploadWaitingUrlArr.firstObject; customUploadOperation * operation = [customUploadOperation new]; operation.fileModel = WaitingModel; //等待下载中的任务 [self.uploadWaitingUrlArr removeObjectAtIndex:0]; HLog(@"添加一个新的上传任务 %@",self.uploadingOperationArr) //添加到下载中数组 [self.uploadingOperationArr addObject:operation]; //[self checkFileUploadStateWithOperation:operation]; [newTaskArr addObject:operation]; //[weakSelf handleTaskDidUploadWith:operation withState:state]; } } //开始检测新的上传任务 for (customUploadOperation * operation in newTaskArr) { [self checkFileUploadStateWithOperation:operation]; } [newTaskArr removeAllObjects]; } } #pragma mark 检测文件是否上传过了 - (void)checkFileUploadStateWithOperation:(customUploadOperation*)operation { HLog(@"%@检测文件是否过--%ld",operation.fileModel.filename,operation.fileModel.totalBytes) NSMutableDictionary*paraDict = [NSMutableDictionary new]; //operation.fileModel.filename = @"Img_4107.png";// test same name file if(operation.fileModel.savePath){ NSString *absPath = [[NSString alloc] initWithFormat:@"%@%@",operation.fileModel.savePath,operation.fileModel.filename]; [paraDict setValue:absPath forKey:@"absPath"]; NSNumber *totalBytesNumber = [NSNumber numberWithLong:operation.fileModel.totalBytes]; [paraDict setValue:totalBytesNumber forKey:@"fileSize"]; } KWeakSelf [[netWorkManager shareInstance] cloudPhoneGETCallBackCode:@"isFileExist" Parameters:paraDict success:^(id _Nonnull responseObject) { frpFileExistModel *model = [[frpFileExistModel alloc] initWithDictionary:responseObject error:nil]; if(model && model.status == 0){ [weakSelf checkFileUploadStateFunAfterNetWith:model WithOperation:operation]; } else if(model && model.status == 5){//云机空间不足 //上传路径 NSString * backupsDefaultPath = [HWDataManager getStringWithKey:stringKeyAddSn(Const_photo_upload_default_path)]; NSArray * stringArr = [backupsDefaultPath componentsSeparatedByString:@"/"]; NSString *lastStr = stringArr.lastObject; NSString *repStr = [[NSString alloc] initWithFormat:@"/%@",lastStr]; NSString *name = [backupsDefaultPath stringByReplacingOccurrencesOfString:repStr withString:@""]; name = [iTools changePathToShowPathBy:name]; NSString* showTip = [[NSString alloc] initWithFormat:@"【%@】%@",name,NSLocalizedString(@"disk_space_not_tip_for_95",nil)]; [[iToast makeText:showTip] show]; [weakSelf handleUploadFailOneFileBy:operation]; } else{ HLog(@"isFileExist接口异常:%@",responseObject) [weakSelf handleUploadFailOneFileBy:operation]; } } failure:^(NSError * _Nonnull error) { HLog(@"%@",error) [weakSelf handleUploadFailOneFileBy:operation]; }]; } #pragma mark 检测分家是否存在后处理 是否要上传 - (void)checkFileUploadStateFunAfterNetWith:(frpFileExistModel*)model WithOperation:(customUploadOperation*)operation { if(!model){ [self handleUploadFailOneFileBy:operation]; return; } operation.fileModel.didUploadBytes = 0; operation.fileModel.taskId = model.data.fileTaskId; if(!model.data.exist){//未上传过 HLog(@"%@检测到文件未上传过--%ld",operation.fileModel.filename,operation.fileModel.totalBytes) [self prepareToUploadFileWithOperation:operation]; } else if(model.data.isComplete){//上传过了 并且文件上传完了 //判断下文件创建长度是否一致 一致则是上传完了 不一致 重新上传一个 可能是同名的文件而已 // if(model.data.size >= operation.fileModel.totalBytes){//上传完了 // [self handleFileDidUploadFunWithOperation:operation]; // } // else{//未上传完 isComplete 这个字段有问题 // operation.fileModel.didUploadBytes = model.data.size; // [self prepareToUploadFileWithOperation:operation]; // } HLog(@"%@检测到文件上传完成--%ld",operation.fileModel.filename,operation.fileModel.totalBytes) //修复同名文件丢失问题 if(model.data.size == operation.fileModel.totalBytes){//上传完了 HLog(@"1111111") [self handleFileDidUploadFunWithOperation:operation]; } else{//同名文件 处理 HLog(@"2222222") [self prepareToUploadFileWithOperation:operation]; } } else{//上传过了 未上传完成 HLog(@"%@检测到文件上传过了 未上传完成--%ld",operation.fileModel.filename,operation.fileModel.totalBytes) operation.fileModel.didUploadBytes = model.data.size; [self prepareToUploadFileWithOperation:operation]; } } #pragma mark 任务上传过了了 - (void)handleFileDidUploadFunWithOperation:(customUploadOperation*)operation { // mainBlock(^{ // [[iToast makeText:NSLocalizedString(@"File_upload_file_already_exists",nil)] show]; // }); // // NSMutableArray *delArr = [NSMutableArray new]; // [delArr addObject:operation.fileModel]; // // [[nasUploadFileManager shareInstance] deleteUploadFileRecordBy:delArr withDelCache:YES complete:^(BOOL isSuccess) { // if (isSuccess) { // // } // }]; //改为走上传完成的逻辑 [self handleUploadDoneOneFileBy:operation]; } //加个接口 做删除用的 - (void)deleteUploadFileRecordWithOperation:(customUploadOperation*)operation{ NSMutableArray *delArr = [NSMutableArray new]; [delArr addObject:operation.fileModel]; [[nasUploadFileManager shareInstance] deleteUploadFileRecordBy:delArr withDelCache:YES complete:^(BOOL isSuccess) { if (isSuccess) { } }]; } #pragma mark 准备上传文件 - (void)prepareToUploadFileWithOperation:(customUploadOperation*)operation { NSMutableDictionary *paraDict = [NSMutableDictionary new]; NSString* taskUid = operation.fileModel.taskId; if(!taskUid || taskUid.length == 0){ taskUid = [iTools getTaskUidStr]; operation.fileModel.taskId = taskUid; } [paraDict setObject:taskUid forKey:@"taskId"]; [paraDict setObject:@0 forKey:@"position"]; [paraDict setObject:@"true" forKey:@"isLast"]; if(operation.fileModel.savePath){ [paraDict setObject:operation.fileModel.savePath forKey:@"savePath"]; } else{ HLog(@"获取保存路径失败") [self handleUploadFailOneFileBy:operation]; return; } if(operation.fileModel.filename){ [paraDict setObject:operation.fileModel.filename forKey:@"filename"]; } else{ HLog(@"获取用户名失败") return; } KWeakSelf if(operation.fileModel.curUploadFileType == uploadFileTypeImage){ [paraDict setObject:@1 forKey:@"imageType"]; if(!operation.fileModel.imageData){ NSString*pathStr = [cachesFileManager getFilePathWithName:operation.fileModel.filename type:operation.fileModel.curUploadFileType]; NSData *imageData = [NSData dataWithContentsOfFile:pathStr]; //修复出现过 图片数据小于totalBytes if(imageData){ long curDataLengt = [imageData length]; if(curDataLengt == operation.fileModel.didUploadBytes && curDataLengt < operation.fileModel.totalBytes){ HLog(@"totalBytes 获取出错?") [self deleteUploadFileRecordWithOperation:operation]; return; } } if (!imageData) { if(!operation.fileModel.asset){ NSString *curLocalIdentifier = operation.fileModel.localIdentifier; PHFetchResult *fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[curLocalIdentifier] options:nil]; PHAsset *asset = fetchResult.firstObject; operation.fileModel.asset = asset; } [[PHImageManager defaultManager] requestImageDataForAsset:operation.fileModel.asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) { // 直接得到最终的 NSData 数据 if (imageData) { operation.fileModel.imageData = imageData; if(operation.fileModel.totalBytes == 0){ operation.fileModel.totalBytes = [imageData length]; } [weakSelf afterGetImageDataFunWithOperation:operation]; } else{ HLog(@"\n\n\n\nrequestImageDataForAsset fail imageData nil\n\n\n") [weakSelf handleUploadFailOneFileBy:operation]; } }]; return; } operation.fileModel.imageData = imageData; } HLog(@"%@开始上传--%ld",operation.fileModel.filename,operation.fileModel.totalBytes) NSData *curData = operation.fileModel.imageData; operation.onceDataLengt = [curData length]; if(operation.fileModel.totalBytes == 0){//异常处理 operation.fileModel.totalBytes = operation.onceDataLengt; } [self startUpload:paraDict operation:operation data:curData success:^(id _Nonnull responseObject) { frpUploadModel *model = [[frpUploadModel alloc] initWithDictionary:responseObject error:nil]; if(model && model.position == operation.fileModel.totalBytes && model.position != 0){ HLog(@"%@上传完成 000---%ld",operation.fileModel.filename,operation.fileModel.totalBytes) [weakSelf handleUploadDoneOneFileBy:operation]; } else{ HLog(@"%@上传完成异常 %ld---%ld",operation.fileModel.filename,model.position,operation.fileModel.totalBytes) [weakSelf handleUploadDoneOneFileBy:operation]; } } faild:^(NSError * _Nonnull error) { HLog(@"%@上传失败",operation.fileModel.filename) [weakSelf handleUploadFailOneFileBy:operation]; }]; } else if(operation.fileModel.curUploadFileType == uploadFileTypeVideo){ [paraDict setObject:@1 forKey:@"videoType"]; [paraDict setObject:@"false" forKey:@"isLast"]; if(![cachesFileManager checkFileIsSaveState:operation.fileModel.filename withType:uploadFileTypeVideo]){ if(!operation.fileModel.asset){ NSString *curLocalIdentifier = operation.fileModel.localIdentifier; PHFetchResult *fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[curLocalIdentifier] options:nil]; PHAsset *asset = fetchResult.firstObject; operation.fileModel.asset = asset; } //真正的视频数据 PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init]; options.version = PHVideoRequestOptionsVersionOriginal; [[PHImageManager defaultManager] requestAVAssetForVideo:operation.fileModel.asset options:options resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) { if ([asset isKindOfClass:[AVURLAsset class]]) { AVURLAsset* urlAsset = (AVURLAsset*)asset; BOOL isSuc = [cachesFileManager copyVideoItemAtPath:[urlAsset.URL path] fileName:operation.fileModel.filename error:nil]; if (isSuc) { [weakSelf afterGetVideoDataFunWithOperation:operation]; } else{ [weakSelf handleUploadFailOneFileBy:operation]; } } else{ [weakSelf handleUploadFailOneFileBy:operation]; } }]; return; } long curPosition = operation.fileModel.didUploadBytes; [self beginUploadVideoDataFunBy:operation with:curPosition withPara:paraDict success:^(id _Nonnull responseObject) { } faild:^(NSError * _Nonnull error) { }]; } else if(operation.fileModel.curUploadFileType == uploadFileTypeFileAPP){ //上传文件 [paraDict setObject:@3 forKey:@"videoType"]; [paraDict setObject:@"false" forKey:@"isLast"]; if(![cachesFileManager checkFileIsSaveState:operation.fileModel.filename withType:uploadFileTypeFileAPP]){ [weakSelf handleUploadFailOneFileBy:operation]; return; } long curPosition = operation.fileModel.didUploadBytes; [self beginUploadVideoDataFunBy:operation with:curPosition withPara:paraDict success:^(id _Nonnull responseObject) { } faild:^(NSError * _Nonnull error) { }]; } else if(operation.fileModel.curUploadFileType == uploadFileTypeRecord){ //上传文件 [paraDict setObject:@4 forKey:@"videoType"]; [paraDict setObject:@"false" forKey:@"isLast"]; if(![cachesFileManager checkFileIsSaveState:operation.fileModel.filename withType:uploadFileTypeRecord]){ [weakSelf handleUploadFailOneFileBy:operation]; return; } long curPosition = operation.fileModel.didUploadBytes; [self beginUploadVideoDataFunBy:operation with:curPosition withPara:paraDict success:^(id _Nonnull responseObject) { } faild:^(NSError * _Nonnull error) { }]; } } #pragma mark 视频上传 或者文件APP上传 - (void)beginUploadVideoDataFunBy:(customUploadOperation*)operation with:(NSInteger)position withPara:(NSMutableDictionary*)paraDict success:(netWork_Success)success faild:(netWork_Faild)faildStr { if(operation.isCancelType){ HLog(@"防止取消不了任务生效") return; } BOOL isLastPicece = NO; if((operation.fileModel.totalBytes - position) <= MaxNasUploadPieceSzie){ [paraDict setObject:@"true" forKey:@"isLast"]; isLastPicece = YES; } else{ [paraDict setObject:@"false" forKey:@"isLast"]; } [paraDict setObject:[NSNumber numberWithLong:position] forKey:@"position"]; //视频数据切片 __block NSData *videoData = [self cutVideoFileFunAtIndex:position withMaxLenght:MaxNasUploadPieceSzie withModel:operation.fileModel withOperation:operation]; if(!videoData ||videoData.length ==0){ HLog(@"视频没获取到") [self handleUploadFailOneFileBy:operation]; return; } operation.onceDataLengt = [videoData length]; KWeakSelf [self startUpload:paraDict operation:operation data:videoData success:^(id _Nonnull responseObject) { frpUploadModel *model = [[frpUploadModel alloc] initWithDictionary:responseObject error:nil]; if(model && model.position >= operation.fileModel.totalBytes){ HLog(@"%@上传完成 001",operation.fileModel.filename) [weakSelf handleUploadDoneOneFileBy:operation]; } else{ HLog(@"%@上传完成一片 %ld",operation.fileModel.filename,model.position) [weakSelf beginUploadVideoDataFunBy:operation with:model.position withPara:paraDict success:^(id _Nonnull responseObject) { success(responseObject); } faild:^(NSError * _Nonnull error) { NSError *err = error; if(error.code != -999){ faildStr(err); } }]; } } faild:^(NSError * _Nonnull error) { HLog(@"%@上传失败",operation.fileModel.filename) [weakSelf handleUploadFailOneFileBy:operation]; }]; } #pragma mark 分段读视频文件 -(NSData*)cutVideoFileFunAtIndex:(NSUInteger)dataIndex withMaxLenght:(NSInteger)maxLengt withModel:(uploadFileDataModel*)dataModel withOperation:(customUploadOperation*)operation{ //return [operation cutVideoFileFunAtIndex:dataIndex withMaxLenght:maxLengt]; HLog(@"视频切片开始 线程:%@",[NSThread currentThread]); //视频 NSString *filePath = [cachesFileManager getFilePathWithName:dataModel.filename type:uploadFileTypeVideo]; // 文件路径 //文件APP if (dataModel.curUploadFileType == uploadFileTypeFileAPP) { filePath = [cachesFileManager getFilePathWithName:dataModel.filename type:uploadFileTypeFileAPP]; // 文件路径 } else if (dataModel.curUploadFileType == uploadFileTypeRecord) { filePath = [cachesFileManager getFilePathWithName:dataModel.filename type:uploadFileTypeRecord]; // 文件路径 } NSFileManager *manager0 = [NSFileManager defaultManager]; if(![manager0 fileExistsAtPath:filePath]) { return [NSData new]; } NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath]; // 创建文件句柄 // 设置分段读取的大小,这里以每次读取1KB为例 const NSUInteger chunkSize = maxLengt;//cutVideoPieceSzie;//5 * 1024 *1024; NSMutableData *data = [NSMutableData data]; if (fileHandle) { long long endOfFile = [fileHandle seekToEndOfFile]; if(dataModel.totalBytes == 0 || dataModel.totalBytes < endOfFile){//异常处理 dataModel.totalBytes = endOfFile; } //异常处理 if(endOfFile == dataIndex){ dataModel.totalBytes = endOfFile; dataModel.didUploadBytes = endOfFile; dataModel.curUploadStateType = uploadStateDone; [fileHandle closeFile]; return data; } if (endOfFile >= chunkSize) { // 读取文件的分段数据到某个位置 [fileHandle seekToFileOffset:dataIndex]; // 读取文件的分段数据 NSData* chunk = [fileHandle readDataOfLength:chunkSize]; if (chunk) { [data appendData:chunk]; } } else{ // 读取文件的分段数据到某个位置 [fileHandle seekToFileOffset:dataIndex]; [data appendData:[fileHandle readDataToEndOfFile]]; } // 在这里可以对文件内容进行处理 // ... // 关闭文件句柄 [fileHandle closeFile]; } HLog(@"视频切片完成 dataIndex:%ld --长度:%ld",dataIndex,[data length]) return data; } #pragma mark 处理上传完成 - (void)handleUploadDoneOneFileBy:(customUploadOperation*)operation { HLog(@"handleUploadDoneOneFileBy") [[NSNotificationCenter defaultCenter] postNotificationName:nasUploadTaskExeEnd object:operation.fileModel]; [self beginUploadAfterDeleteOperationBy:operation]; } #pragma mark 处理删除失败 - (void)handleUploadFailOneFileBy:(customUploadOperation*)operation { operation.fileModel.curUploadStateType = uploadStateFail; [[NSNotificationCenter defaultCenter] postNotificationName:nasUploadTaskExeError object:operation.fileModel]; [self beginUploadAfterDeleteOperationBy:operation]; } #pragma mark 根据 asset 获取到图片数据 - (void)afterGetImageDataFunWithOperation:(customUploadOperation*)operation { [cachesFileManager getFileNameWithContent:operation.fileModel.imageData fileName:operation.fileModel.filename type:uploadFileTypeImage]; [self prepareToUploadFileWithOperation:operation]; } #pragma mark 根据 asset 获取到视频数据 - (void)afterGetVideoDataFunWithOperation:(customUploadOperation*)operation { [self prepareToUploadFileWithOperation:operation]; } #pragma mark 开始上传 - (void)startUpload:(NSMutableDictionary *)params operation:(customUploadOperation*)operation data:(NSData *)data success:(netWork_Success)success faild:(netWork_Faild)faildStr { NSString *urlString = ksharedAppDelegate.NASFileByBoxService; urlString = [[NSString alloc] initWithFormat:@"%@uploadFile",urlString]; NSURL *URL = [NSURL URLWithString:urlString]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; [request setHTTPMethod:@"POST"]; //请求体 NSMutableData *bodyData = [self getBodyDataWithRequest:request withModel:operation.fileModel withData:data withPara:params]; //设置请求体 [request setHTTPMethod:@"POST"]; [request setHTTPBody:bodyData]; //设置请求体长度 NSInteger length = [bodyData length]; [request setValue:[NSString stringWithFormat:@"%ld",length] forHTTPHeaderField:@"Content-Length"]; //设置 POST请求文件上传 [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",Kboundary2] forHTTPHeaderField:@"Content-Type"]; KWeakSelf //回话对象 NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil]; operation.session = session; //请求task /* 第一个参数:请求对象 第二个参数:传递是要上传的数据(请求体) 第三个参数: */ NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:nil completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { //解析 [weakSelf handleCustomUploadResultBy:data withResponse:response withError:error success:success faild:faildStr]; }]; // NSString *filePath = [cachesFileManager getFilePathWithName:dataModel.filename type:uploadFileTypeVideo]; // 文件路径 // NSURL *filePathUrl = [NSURL URLWithString:filePath]; // // NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:filePathUrl completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { // HLog(@"data string:%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]); //[weakSelf handleCustomUploadResultBy:data withResponse:response withError:error]; // }]; // NSURLSessionDataTask *uploadTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { // // HLog(@"data string:%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]); //[weakSelf handleCustomUploadResultBy:data withResponse:response withError:error]; // //// NSJSONSerialization *object = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil]; //// NSDictionary *dict = (NSDictionary *)object; //// NSLog(@"=====%@",[dict objectForKey:@"success"]); // }]; //执行Task [uploadTask resume]; operation.dataTask = uploadTask; } #pragma mark 用系统方法写的上传处理 - (void)handleCustomUploadResultBy:(NSData*)data withResponse:(NSURLResponse*)response withError:(NSError*)error success:(netWork_Success)success faild:(netWork_Faild)faildStr{ if(error){ HLog(@"上传错误:%@",error) // -1005 网络中断 1009 网络似乎中断 if(error.code == -1005 ||error.code == -1009){//网络中断 [[nasUploadFileManager shareInstance] saveUploadingTaskByNetWorkErrorFun]; return; } if(error.code != -999){ faildStr(error); } return; } NSJSONSerialization *object = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil]; NSDictionary *dict = (NSDictionary *)object; frpUploadModel *model = [[frpUploadModel alloc] initWithDictionary:dict error:nil]; if(model && [model.msg isEqualToString:@"success"]){ success(dict); } else{ NSError *err = [NSError new]; faildStr(err); } } - (NSMutableData *)getBodyDataWithRequest:(NSMutableURLRequest *)request withModel:(uploadFileDataModel*)dataModel withData:(NSData*)data withPara:(NSMutableDictionary*)params{ //1 边界符号要配置请求头里面去 /* multipart/form-data 是表单格式 charset=utf-8 是utf-8编码 bounary 是表单开头 */ [request setValue:[NSString stringWithFormat:@"multipart/form-data; charset=utf-8; boundary=%@", Kboundary2] forHTTPHeaderField:@"Content-Type"]; /// body NSMutableData *boydData = [NSMutableData data]; // 2.1 边界符号(开始边界) //2.1.1 其它参数 NSMutableString *paraString = [NSMutableString new]; //[paraString appendFormat:@"--%@\r\n",Kboundary];//\n:换行 \n:切换到行首 for (NSString *key in params) { NSString *value = params[key]; [paraString appendFormat:@"--%@\r\n",Kboundary2];//\n:换行 \n:切换到行首 [paraString appendFormat:@"Content-Disposition: form-data; name=\"%@\"",key]; [paraString appendFormat:@"\r\n"]; [paraString appendFormat:@"\r\n"]; [paraString appendFormat:@"%@\r\n",value]; } //[boydData appendData:[paraString dataUsingEncoding:NSUTF8StringEncoding]]; // body每一个段内容以换行符作为结束标示 NSString *fileBeginBoundary = [NSString stringWithFormat:@"--%@\r\n", Kboundary2]; //[boydData appendData:[fileBeginBoundary dataUsingEncoding:NSUTF8StringEncoding]]; [paraString appendString:fileBeginBoundary]; // 2.2 属性配置 名字;key;类型 NSString *serverFileKey = @"image"; //key //NSString *serverFileKey = @"file"; NSString *serverContentTypes = @"image/png"; //类型 if (dataModel.curUploadFileType == uploadFileTypeVideo) { serverFileKey = @"video"; serverContentTypes = @"video/mp4"; } NSString *serverFileName = dataModel.filename; //name // filename已命名文件; name相当于一个key, 这个名字和服务器保持一致 /* 理解key,表单发送给服务端,服务端拿到数据之后,可以将任务解析成一个字典了imageDict;图片数据会通过这个字典里面的name来获取图片(伪代码 image = imageDict[serverFileKey]) */ //2.3 拼接数据(创建一个字符串来拼装) NSMutableString *string = [NSMutableString new]; [string appendFormat:@"Content-Disposition:form-data; name=\"%@\"; filename=\"%@\" ", @"file", serverFileName]; //[string appendFormat:@"%@", KNewLine]; [string appendFormat:@"\r\n"]; [string appendFormat:@"Content-Type:%@", serverContentTypes]; // [string appendFormat:@"%@", KNewLine]; // [string appendFormat:@"%@", KNewLine]; [string appendFormat:@"\r\n"]; [string appendFormat:@"\r\n"]; // [boydData appendData:[string dataUsingEncoding:NSUTF8StringEncoding]]; [paraString appendString:string]; [boydData appendData:[paraString dataUsingEncoding:NSUTF8StringEncoding]]; // 2.3 拼接数据(拼接文件数据) [boydData appendData:data]; // 2.4 边界符号 (结束边界) NSString *fileEndBoundary = [NSString stringWithFormat:@"\r\n--%@--", Kboundary2]; [boydData appendData:[fileEndBoundary dataUsingEncoding:NSUTF8StringEncoding]]; return boydData; } #pragma mark 上传的文件已存在 - (void)handleTaskDidUploadWith:(customUploadOperation*)operation withState:(NSInteger)state { if(state == 1){ [[iToast makeText:NSLocalizedString(@"File_upload_file_already_exists",nil)] show]; } HLog(@"上传的文件已存在 删除任务:_uploadingOperationArr:%@",_uploadingOperationArr); [self.uploadingOperationArr removeObject:operation]; NSMutableArray *delArr = [NSMutableArray new]; [delArr addObject:operation.fileModel]; [[nasUploadFileManager shareInstance] deleteUploadFileRecordBy:delArr withDelCache:NO complete:^(BOOL isSuccess) { if (isSuccess) { } }]; [self beginUploadAfterDeleteOperationBy:nil]; } #pragma mark - // ssl 服务 证书信任 - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{ if(![challenge.protectionSpace.authenticationMethod isEqualToString:@"NSURLAuthenticationMethodServerTrust"]) { return; } // 信任该插件 NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust]; // 第一个参数 告诉系统如何处置 completionHandler(NSURLSessionAuthChallengeUseCredential,credential); } //当请求协议是https的时候回调用该方法 //Challenge 挑战 质询(受保护空间) //NSURLAuthenticationMethodServerTrust 服务器信任证书 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler { if(![challenge.protectionSpace.authenticationMethod isEqualToString:@"NSURLAuthenticationMethodServerTrust"]) { return; } // 信任该插件 NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust]; // 第一个参数 告诉系统如何处置 completionHandler(NSURLSessionAuthChallengeUseCredential,credential); } // 接受到响应调用 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler { completionHandler(NSURLSessionResponseAllow); } // 上传进度 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{ //每包发送的大小bytesSent,totalBytesSent已经上传了多少;totalBytesExpectedToSend总共要发送多少 // 32768 = 32KB HLog(@"didSendBodyData: %lld--%lld-%lld", bytesSent, totalBytesSent, totalBytesExpectedToSend); @synchronized (self) { for (customUploadOperation*operation in _uploadingOperationArr) { HLog(@"正在遍历数组 _uploadingOperationArr:%@",_uploadingOperationArr); if(operation.dataTask == task){ operation.fileModel.didUploadBytes += bytesSent; //调整下大小 因为上传的数据 除了data 长度 还包含了 参数大小 if(totalBytesSent == totalBytesExpectedToSend){ operation.fileModel.didUploadBytes -= (totalBytesExpectedToSend - operation.onceDataLengt); } NSTimeInterval curTime = [[NSDate date] timeIntervalSince1970]; NSTimeInterval timeDiff = curTime - operation.preNotTimeInterval; //HLog(@"控制刷新时间为1秒:%f",timeDiff); CGFloat RefreshTimer = 0.8; if(operation.fileModel.totalBytes <= 5*1024*1024){//小于5M 事实 RefreshTimer = 0.2; } if (operation.preNotTimeInterval <= 0 || timeDiff > RefreshTimer ) { operation.preNotTimeInterval = curTime; [[NSNotificationCenter defaultCenter] postNotificationName:uploadFileUploadingNotification object:operation.fileModel]; } break; } } } } #pragma mark 取消单个任务 - (void)cancelUploadTaskFunWith:(uploadFileDataModel*)fileModel { HLog(@"取消任务-- %@--%@",fileModel.filename,fileModel.taskId) @synchronized (self) { for (customUploadOperation*operation in _uploadingOperationArr) { HLog(@"正在遍历数组 _uploadingOperationArr:%@",_uploadingOperationArr); if([operation.fileModel.localIdentifier isEqualToString:fileModel.localIdentifier]){ operation.isCancelType = YES; [operation.dataTask cancel]; if(operation.fileHandle){ [operation closeFileHandleFun]; } [self beginUploadAfterDeleteOperationBy:operation]; return; } } for (uploadFileDataModel*waitModel in _uploadWaitingUrlArr) { if(//[waitModel.filename isEqualToString:fileModel.filename] [waitModel.localIdentifier isEqualToString:fileModel.localIdentifier]){ [_uploadWaitingUrlArr removeObject:waitModel]; [self beginUploadAfterDeleteOperationBy:nil]; return; } } } } #pragma mark 取消所有任务 - (void)cancelUploadAllTaskFun { HLog(@"取消所有任务") @synchronized (self) { [_uploadWaitingUrlArr removeAllObjects]; for (customUploadOperation*operation in _uploadingOperationArr) { HLog(@"正在遍历数组 _uploadingOperationArr:%@",_uploadingOperationArr); operation.isCancelType = YES; [operation.dataTask cancel]; if(operation.fileHandle){ [operation closeFileHandleFun]; } } [_uploadingOperationArr removeAllObjects]; } } #pragma mark 判断是否在上传中 - (BOOL)checkUploadTaskDoingFun { if(_uploadWaitingUrlArr.count >0 || _uploadingOperationArr.count>0){ return YES; } return NO; } - (BOOL)isUploadIngType { if(self.uploadWaitingUrlArr.count >0){ return YES; } if(self.uploadingOperationArr.count >0){ return YES; } return NO; } #pragma mark - lazy load - (NSMutableArray *)uploadWaitingUrlArr { if (!_uploadWaitingUrlArr) { _uploadWaitingUrlArr = [NSMutableArray array]; } return _uploadWaitingUrlArr; } - (NSMutableArray *)uploadingOperationArr { if (!_uploadingOperationArr) { _uploadingOperationArr = [NSMutableArray array]; } return _uploadingOperationArr; } @end