// // webSocketManager+upLoadFile.m // 双子星云手机 // // Created by xd h on 2024/6/20. // #import "webSocketManager+upLoadFile.h" @implementation webSocketManager (upLoadFile) #pragma mark 监听文件开始的的通知 - (void)beginGotoUploadFileFunBy:(uploadFileDataModel *)uploadFileDataMod { self.isSuspendUploadType = NO; //HLog(@"%@",uploadFileDataMod); self.curUploadFileDataMod = uploadFileDataMod; if(![uploadFileDataMod isKindOfClass:[uploadFileDataMod class]]){ HLog(@"上传的文件类型不对"); return; } [self ApplyUploadFileFun]; } - (void)applyUploadFileServiceResponseFun:(NSDictionary *)dataDict { if([dataDict isKindOfClass:[NSDictionary class]]){ if ([[dataDict allKeys] containsObject:@"data"]) { NSDictionary *data = dataDict[@"data"]; if ([[data allKeys] containsObject:@"status"]) { NSString *status = [data objectForKey:@"status"]; if(![status isKindOfClass:[NSString class]]){ status = [[NSString alloc] initWithFormat:@"%@",status]; } //if(1) if([status isEqualToString:@"1"]) { //[self tryAgainApplyUploadFun]; [self clearUploadFileDataFun]; [[uploadFileManager shareInstance] uploadFileFailFun]; }else if ([status isEqualToString:@"0"]){ if ([[data allKeys] containsObject:@"taskUid"]) { NSString *taskUid = data[@"taskUid"]; if(![status isKindOfClass:[NSString class]]){ taskUid = [[NSString alloc] initWithFormat:@"%@",taskUid]; } if(taskUid && [taskUid isEqualToString:self.taskUid]){ NSNumber * fileLengthNum = @0; if ([[data allKeys] containsObject:@"fileLength"]) { fileLengthNum = data[@"fileLength"]; } // [[uploadFileManager shareInstance] changeUploadFileState:uploadStateUploading withDidUploadBytes:[fileLengthNum integerValue] withModel:self.curUploadFileDataMod complete:^(BOOL isSuccess) { // // }]; if([fileLengthNum integerValue] == self.curUploadFileDataMod.totalBytes && self.curUploadFileDataMod.totalBytes != 0){ self.taskUid = nil; //self.curUploadFileDataMod.curUploadStateType = uploadStateDone; NSMutableArray *delArr = [NSMutableArray new]; [delArr addObject:self.curUploadFileDataMod]; [[uploadFileManager shareInstance] deleteUploadFileRecordBy:delArr withDelCache:NO complete:^(BOOL isSuccess) { if (isSuccess) { } }]; mainBlock(^{ [[iToast makeText:NSLocalizedString(@"File_upload_file_already_exists",nil)] show]; }); HLog(@"hxd already 上传文件已存在") return; } self.curUploadPostion = [fileLengthNum longValue]; if(self.curUploadFileDataMod.curUploadFileType == uploadFileTypeImage){ [self beginGotoUploadImageFun:fileLengthNum]; } else{ [self beginGotoUploadVidelFun:fileLengthNum]; } } } } } } else{ //异常 [self tryAgainApplyUploadFun]; } } else{ //异常 [self tryAgainApplyUploadFun]; } } #pragma mark 图片上传 -(void)beginGotoUploadImageFun:(NSNumber*)fileLengthNum{ self.cutFileDataArr = [self fileCutPartsBy:self.AllFileData withDidUpLoadSize:[fileLengthNum integerValue]]; if(!self.cutFileDataArr || self.cutFileDataArr.count == 0){ [self clearUploadFileDataFun]; [[uploadFileManager shareInstance] uploadFileFailFun]; HLog(@"hxd fail 上传任务异常终止") return; } //答复的taskUid 跟申请的一致 开发发送内容 [self upLoadFileFun]; } #pragma mark 视频上传 //每次从文件重新切一片 传一片 -(void)beginGotoUploadVidelFun:(NSNumber*)fileLengthNum{ NSData *data = [self cutVideoFileFunAtIndex:fileLengthNum.longLongValue]; HLog(@"data leng:%ld",[data length]); if([data length] == 0){ if(self.curUploadFileDataMod.curUploadStateType == uploadStateDone){ [[uploadFileManager shareInstance] uploadFileDoneFun]; } else{ [[uploadFileManager shareInstance] uploadFileFailFun]; } [self clearUploadFileDataFun]; return; } self.AllFileData = data; self.cutFileDataArr = [self fileCutPartsBy:self.AllFileData withDidUpLoadSize:0]; self.indexOfUploadFlie = 0; //return; // NSString*videoPathStr = [cachesFileManager getFilePathWithName:self.curUploadFileDataMod.filename type:uploadFileTypeVideo]; // self.AllFileData = [NSData dataWithContentsOfFile:videoPathStr]; // self.cutFileDataArr = [self fileCutPartsBy:self.AllFileData withDidUpLoadSize:[fileLengthNum integerValue]]; if(!self.cutFileDataArr ||self.cutFileDataArr.count ==0){ [self clearUploadFileDataFun]; [[uploadFileManager shareInstance] uploadFileFailFun]; HLog(@"hxd fail 上传任务异常终止") return; } //答复的taskUid 跟申请的一致 开发发送内容 [self upLoadFileFun]; } #pragma mark 分段读视频文件 -(NSData*)cutVideoFileFunAtIndex:(NSUInteger)dataIndex{ NSString *filePath = [cachesFileManager getFilePathWithName:self.curUploadFileDataMod.filename type:uploadFileTypeVideo]; // 文件路径 NSFileManager *manager0 = [NSFileManager defaultManager]; if(![manager0 fileExistsAtPath:filePath]) { return [NSData new]; } NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath]; // 创建文件句柄 // 设置分段读取的大小,这里以每次读取1KB为例 //const NSUInteger chunkSize = 50 * 1024 *1024; const NSUInteger chunkSize = cutVideoPieceSzie;//5 * 1024 *1024; NSMutableData *data = [NSMutableData data]; if (fileHandle) { long long endOfFile = [fileHandle seekToEndOfFile]; if(self.curUploadFileDataMod.totalBytes == 0 || self.curUploadFileDataMod.totalBytes < endOfFile){//异常处理 self.curUploadFileDataMod.totalBytes = endOfFile; } //异常处理 if(endOfFile == dataIndex){ self.curUploadFileDataMod.totalBytes = endOfFile; self.curUploadFileDataMod.didUploadBytes = endOfFile; self.curUploadFileDataMod.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]; } return data; } - (void)upLoadFileFunServiceResponseFun:(NSDictionary *)dataDict { //{"type":"uploadFileRandomRet","data":{"progress":"9\/10","filePath":"/sdcard/Download/IMG_0036.JPG","status":"0","msg":"success"}} if([dataDict isKindOfClass:[NSDictionary class]]){ if ([[dataDict allKeys] containsObject:@"data"]) { NSDictionary *data = dataDict[@"data"]; if ([[data allKeys] containsObject:@"taskId"]){ NSString *taskId = data[@"taskId"]; if(![taskId isEqualToString:self.taskUid]){ return; } } if ([[data allKeys] containsObject:@"status"]) { NSString *status = [data objectForKey:@"status"]; if(![status isKindOfClass:[NSString class]]){ status = [[NSString alloc] initWithFormat:@"%@",status]; } if ([status isEqualToString:@"0"]){ NSNumber * fileLengthNum = @0; if ([[data allKeys] containsObject:@"fileLenth"]) { fileLengthNum = data[@"fileLenth"]; } if (fileLengthNum && fileLengthNum.longLongValue >= self.curUploadFileDataMod.totalBytes) {// //告诉代理 完成了 NSString *commandStr = [RCCommandHelp uploadOrBackupsDoneBy:self.taskUid withFileName:self.fileName withType:YES]; //[self.commandChannelManager rc_sendData:commandStr]; [self clearUploadFileDataFun]; [[uploadFileManager shareInstance] uploadFileDoneFun]; } else{ //异常处理 if([fileLengthNum longValue] - self.curUploadPostion > EachPieceSzie){ HLog(@"发现上传后返回的长度 出错"); if(self.curUploadFileDataMod.curUploadFileType == uploadFileTypeVideo) {//清除已经取出来的数据 后面会根据fileLengthNum 重新拿数据 self.cutFileDataArr = [NSMutableArray new]; } else{ NSInteger curIndex = [fileLengthNum longValue] % EachPieceSzie; //把当前的上传位置指定已经上传的前一片,后续++ self.indexOfUploadFlie = curIndex -1; } } self.curUploadPostion = [fileLengthNum longValue]; [self handleUploadingFun:fileLengthNum]; } } else if ([status isEqualToString:@"2"]) { [self clearUploadFileDataFun]; [[uploadFileManager shareInstance] suspendUploadFileFun:YES withModel:nil]; NSString *tipStr = NSLocalizedString(@"disk_not_find_tip",nil); mainBlock(^{ [[iToast makeText:tipStr] show]; }); } else if ([status isEqualToString:@"5"]) { [self clearUploadFileDataFun]; [[uploadFileManager shareInstance] suspendUploadFileFun:YES withModel:nil]; NSString *tipStr = NSLocalizedString(@"disk_space_not_tip",nil); mainBlock(^{ [[iToast makeText:tipStr] show]; }); } else //if ([status isEqualToString:@"1"]) { //[self tryAgainApplyUploadFun]; [self clearUploadFileDataFun]; [[uploadFileManager shareInstance] uploadFileFailFun]; } } } else{ //异常 [self tryAgainApplyUploadFun]; } } else{ //异常 [self tryAgainApplyUploadFun]; } } #pragma mark 申请上传文件 - (void)ApplyUploadFileFun { //if(!self.taskUid){ self.taskUid = [iTools getTaskUidStr]; if([self.taskUid isEqualToString:self.downloadFileTaskUid] ||[self.taskUid isEqualToString:self.backupsTaskUid] ||[self.taskUid isEqualToString:self.downloadThumbnailTaskUid]) {//重新生成taskUid [self ApplyUploadFileFun]; return; } self.fileName = self.curUploadFileDataMod.filename; if(self.curUploadFileDataMod.curUploadFileType == uploadFileTypeVideo){ //self.AllFileData = self.curUploadFileDataMod.videoData; } else{ self.AllFileData = self.curUploadFileDataMod.imageData; } self.indexOfUploadFlie = 0; //HLog(@"%@ %@",self.taskUid,self.fileName); //self.cutFileDataArr = [self fileCutPartsBy:self.AllFileData]; //} NSString * commandStr = [RCCommandHelp applyForUploadFileBy:self.taskUid filePath:self.fileName]; // [self.commandChannelManager rc_sendData:commandStr]; } #pragma mark 遇到异常时 重新上传 - (void)tryAgainApplyUploadFun { self.taskUid = nil; [self ApplyUploadFileFun]; } #pragma mark 处理上传过程 - (void)handleUploadingFun:(NSNumber*)fileLengthNum { //异常处理 if(fileLengthNum.longValue < 0) { return; } BOOL isCanUseCellular = [HWDataManager getBoolWithKey:stringKeyAddSn(Const_file_Transfe_canUse_Cellular_all)]; if(!isCanUseCellular){//不允许流量备份 // if([AFNetworkReachabilityManager sharedManager].networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN){ self.isSuspendUploadType = YES; } } if(self.isSuspendUploadType){ //暂停 self.taskUid = nil; //NSInteger didUploadSize = (self.indexOfUploadFlie+1) * (self.cutFileEachPieceSzie); NSInteger didUploadSize = fileLengthNum.longLongValue; [[uploadFileManager shareInstance] changeUploadFileState:uploadStateSuspend withDidUploadBytes:didUploadSize withModel:self.curUploadFileDataMod complete:^(BOOL isSuccess) { }]; return; } if(self.cutFileDataArr.count > 0 && self.indexOfUploadFlie < (self.cutFileDataArr.count -1)) { //NSInteger didUploadSize = (self.indexOfUploadFlie+1) * (self.cutFileEachPieceSzie); NSInteger didUploadSize = fileLengthNum.longLongValue; [[uploadFileManager shareInstance] changeUploadFileState:uploadStateUploading withDidUploadBytes:didUploadSize withModel:self.curUploadFileDataMod complete:^(BOOL isSuccess) { }]; self.indexOfUploadFlie ++; [self upLoadFileFun]; } else if(self.curUploadFileDataMod.curUploadFileType == uploadFileTypeVideo && fileLengthNum && fileLengthNum.longLongValue < self.curUploadFileDataMod.totalBytes){ //视频做了二次切割 不一定上传完了 HLog(@"视频上传传完一片 再来一片"); [self beginGotoUploadVidelFun:fileLengthNum]; } else{ HLog(@"上传完成"); //告诉代理 完成了 NSString *commandStr = [RCCommandHelp uploadOrBackupsDoneBy:self.taskUid withFileName:self.fileName withType:YES]; //[self.commandChannelManager rc_sendData:commandStr]; [self clearUploadFileDataFun]; [[uploadFileManager shareInstance] uploadFileDoneFun]; } } -(void)clearUploadFileDataFun{ //上传数据清空 self.taskUid = nil; self.fileName = nil; self.AllFileData = nil; self.indexOfUploadFlie = 0; self.cutFileDataArr = nil; self.curUploadFileDataMod = nil; self.isSuspendUploadType = NO; } - (Byte)getBCC:(NSData *)data { Byte bcc = 0; unsigned char *bytes = (unsigned char *)[data bytes]; for (int i = 0; i < [data length]; i++) { bcc = (Byte)(bcc ^ bytes[i]); } return bcc; } #pragma mark 文件切片 -(NSMutableArray *)fileCutPartsBy:(NSData*)curData withDidUpLoadSize:(NSInteger)didLoadSize{ NSData * data = curData; NSUInteger allLength = data.length; NSUInteger subs = EachPieceSzie;//1*1024*1024;//4096;//要切片的大小,我这里设置的是4096字节 //NSUInteger subs = 31*1024;//4096;//要切片的大小,内存没有暴增 self.cutFileEachPieceSzie = subs; NSInteger index = 0;//起始位置 if(didLoadSize != 0){ if (didLoadSize % subs != 0){ //出错了 return nil; } else{ // index = didLoadSize/subs; // allLength = allLength - didLoadSize; self.indexOfUploadFlie = didLoadSize/subs; } } NSMutableArray *dataArray =[NSMutableArray new]; do { //@autoreleasepool { if (allLength>subs) { NSRange range =NSMakeRange(index*subs, subs); index++; //NSLog(@"%@",NSStringFromRange(range)); [dataArray addObject:[data subdataWithRange:range]]; allLength = allLength - subs; }else{ NSRange range = NSMakeRange(index*subs, allLength); //NSLog(@"%@",NSStringFromRange(range)); NSData *subdata = [data subdataWithRange:range]; if(subdata){ [dataArray addObject:subdata]; } allLength = 0; } //} } while (allLength>0); HLog(@"dataArray leng:%ld",dataArray.count);//最后得到切片的结果,数组里面是NSData对象 return dataArray; } // int转NSData - (NSData *)int2Data:(int)i{ Byte b0 = i & 0xff; Byte b1 = i >> 8 & 0xff; Byte b2 = i >> 16 & 0xff; Byte b3 = i >> 24 & 0xff; // 有大小端模式问题? //Byte result[] = {b0, b1, b2, b3}; Byte result[] = {b3, b2, b1, b0}; return [NSData dataWithBytes:result length:sizeof(result)]; } // long转NSData - (NSData *)long2Data:(long)i{ Byte b0 = i & 0xff; Byte b1 = i >> 8 & 0xff; Byte b2 = i >> 16 & 0xff; Byte b3 = i >> 24 & 0xff; Byte b4 = i >> 32 & 0xff; Byte b5 = i >> 40 & 0xff; Byte b6 = i >> 48 & 0xff; Byte b7 = i >> 56 & 0xff; // 有大小端模式问题? //Byte result[] = {b0, b1, b2, b3}; //Byte result[] = {b3, b2, b1, b0}; Byte result[] = {b7, b6, b5,b4,b3, b2, b1, b0}; return [NSData dataWithBytes:result length:sizeof(result)]; } // short转NSData - (NSData *)short2Data:(short)i{ Byte b0 = i & 0xff; Byte b1 = i >> 8 & 0xff; // 有大小端模式问题? //Byte result[] = {b0, b1, b2, b3}; Byte result[] = { b1, b0}; return [NSData dataWithBytes:result length:sizeof(result)]; } #pragma mark 可以发送文件了 /* 文件数据字节流byte[] 格式1:下载日志文件压缩包 |head|msgType|taskId| datalen | curNum | totalNum| data |bcc校验|end| 长度: 1 1 6 (curNum+totalNum+data) 4 4 datalen- 8 1 1 0x68 0x0e 0x16 bcc校验从第1位 也就是从msgType起 到 校验位前(不包含校验位)数据作bcc校验 */ - (void)upLoadFileFun { //HLog(@"上传文件%ld/%ld",self.indexOfUploadFlie +1,self.cutFileDataArr.count); if(self.cutFileDataArr.count <= self.indexOfUploadFlie) { return; } //data NSData *curData = self.cutFileDataArr[self.indexOfUploadFlie]; //HLog(@"headData:%@",curData); //head unsigned char headNum = 104; NSData *headData = [NSMutableData dataWithBytes:&headNum length:sizeof(headNum)]; //HLog(@"headData:%@",headData); //msgType //unsigned char msgType = 12; //unsigned char msgType = 15; unsigned char msgType = 20; NSData *msgTypeData = [NSMutableData dataWithBytes:&msgType length:sizeof(msgType)]; //HLog(@"msgTypeData:%@",msgTypeData); //taskId NSData *taskIdData = [self.taskUid dataUsingEncoding:NSUTF8StringEncoding]; //HLog(@"taskIdData:%@",taskIdData); //datalen //int datalenNum = int([curData length] +(4+4)); int datalenNum = [curData length] + (4+4); //NSData *datalenData = [NSData dataWithBytes:&datalenNum length:sizeof(datalenNum)]; NSData *datalenData = [self int2Data:datalenNum]; //HLog(@"datalenData %@",datalenData); //curNum int curNum = (int)self.indexOfUploadFlie ;//+ 1; //NSData *curNumData = [NSData dataWithBytes:&curNum length:sizeof(curNum)]; NSData * curNumData = [self int2Data:curNum]; //HLog(@"curNumData:%@",curNumData); //totalNum int totalNum = (int)self.cutFileDataArr.count; //NSData *totalNumData = [NSData dataWithBytes:&totalNum length:sizeof(totalNum)]; NSData * totalNumData = [self int2Data:totalNum]; //HLog(@"totalNumData:%@",totalNumData); long Postion = self.curUploadPostion; //long Postion = 100000000000; NSData * PostionData = [self long2Data:Postion]; HLog(@"PostionData:%@",PostionData); NSMutableData *checkBCCData = [NSMutableData new]; [checkBCCData appendData:msgTypeData]; [checkBCCData appendData:taskIdData]; [checkBCCData appendData:datalenData]; [checkBCCData appendData:curNumData]; [checkBCCData appendData:totalNumData]; [checkBCCData appendData:PostionData]; [checkBCCData appendData:curData]; long length = [curData length]; //HLog(@"curData length %d",length); Byte bccCheck = [self getBCC:checkBCCData]; //HLog(@"bcc 000 %d",bccCheck); //bcc校验 char bccNum = (char)bccCheck; NSData *bccData = [NSMutableData dataWithBytes:&bccNum length:sizeof(bccNum)]; //HLog(@"%@",bccData); //end unsigned char endNum = 22; NSData *endData = [NSMutableData dataWithBytes:&endNum length:sizeof(endNum)]; //HLog(@"%@",endData); NSMutableData *sendData = [NSMutableData new]; [sendData appendData:headData]; [sendData appendData:checkBCCData]; [sendData appendData:bccData]; [sendData appendData:endData]; //HLog(@"sendData length %ld",[sendData length]); //[self.commandChannelManager rc_sendData:sendData]; //HLog(@"文件上传sendData %@",sendData); } - (void)suspendUploadFileFun { self.isSuspendUploadType = YES; } @end