// // webSocketManager+backupsFile.m // 双子星云手机 // // Created by xd h on 2024/6/20. // #import "webSocketManager+backupsFile.h" @implementation webSocketManager (backupsFile) /*****************备份相关**********************************/ - (void)beginGotoBackupsFileFunBy:(photosBackupsTaskModel *)photosBackupsTaskMod { //HLog(@"%@",uploadFileDataMod); self.curPhotosBackupsTaskMod = photosBackupsTaskMod; if(![photosBackupsTaskMod isKindOfClass:[photosBackupsTaskModel class]]){ HLog(@"备份的文件类型不对"); return; } [self ApplyBackupsFileFun]; } #pragma mark 申请备份文件 - (void)ApplyBackupsFileFun { //if(!self.taskUid){ self.backupsTaskUid = [iTools getTaskUidStr]; if([self.backupsTaskUid isEqualToString:self.downloadFileTaskUid] ||[self.backupsTaskUid isEqualToString:self.taskUid] ||[self.backupsTaskUid isEqualToString:self.downloadThumbnailTaskUid]) {//重新生成taskUid [self ApplyBackupsFileFun]; return; } self.backupsFileName = self.curPhotosBackupsTaskMod.filename; if(self.curPhotosBackupsTaskMod.curUploadFileType == uploadFileTypeVideo){ } else{ self.backupsAllFileData = self.curPhotosBackupsTaskMod.imageData; } self.backupsIndexOfUploadFlie = 0; //} NSString * backupsDefaultPath = self.curPhotosBackupsTaskMod.backupsPath; if(!backupsDefaultPath || backupsDefaultPath.length ==0){ backupsDefaultPath = [HWDataManager getStringWithKey:stringKeyAddSn(Const_photo_backups_default_path)]; if(!backupsDefaultPath || backupsDefaultPath.length == 0){ backupsDefaultPath = Const_default_backups_path; [HWDataManager setStringWithKey:stringKeyAddSn(Const_photo_backups_default_path) value:backupsDefaultPath]; } NSString *lastStr= [backupsDefaultPath substringFromIndex:backupsDefaultPath.length-1]; if(![lastStr isEqualToString:@"/"]){ backupsDefaultPath = [[NSString alloc] initWithFormat:@"%@/",backupsDefaultPath]; } } NSString * commandStr = [RCCommandHelp applyForBackupsFileBy:self.backupsTaskUid withFileName:self.backupsFileName withSavePath:backupsDefaultPath]; //NSString * commandStr = [RCCommandHelp applyForUploadFileBy:self.taskUid filePath:self.fileName]; //[self.commandChannelManager rc_sendData:commandStr]; } - (void)applyBackupsFileServiceResponseFun:(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 ([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.backupsTaskUid]){ NSNumber * fileLengthNum = @0; if ([[data allKeys] containsObject:@"fileLength"]) { fileLengthNum = data[@"fileLength"]; } self.curBackupsPostion = [fileLengthNum longValue]; if(self.curPhotosBackupsTaskMod.curUploadFileType == uploadFileTypeImage){ if([fileLengthNum integerValue] == self.curPhotosBackupsTaskMod.totalBytes){ self.curPhotosBackupsTaskMod.didBackupsOneFileType = YES; HLog(@"文件已备份"); [[backupsFileManager shareInstance] backupsFileDoneFun]; } else if ([fileLengthNum integerValue] > self.curPhotosBackupsTaskMod.totalBytes){ self.curPhotosBackupsTaskMod.didBackupsOneFileType = YES; HLog(@"云机文件数据大于本地"); [[backupsFileManager shareInstance] getDataWrongToChangeFailFun]; } else{ self.curPhotosBackupsTaskMod.didBackupsOneFileType = NO; [self beginGotoBackupsImageFun:fileLengthNum]; } } else{ [self backupsBeginGotoUploadVidelFun:fileLengthNum]; } } } } else if([status isEqualToString:@"3"]) { NSString *tipStr = NSLocalizedString(@"disk_not_find_tip",nil); [[backupsFileManager shareInstance] changeBackupsFileStateToFailWith:tipStr]; mainBlock(^{ [[iToast makeText:tipStr] show]; }); } else if ([status isEqualToString:@"5"]) { NSString *tipStr = NSLocalizedString(@"disk_space_not_tip",nil); [[backupsFileManager shareInstance] changeBackupsFileStateToFailWith:tipStr]; mainBlock(^{ [[iToast makeText:tipStr] show]; }); } else //if([status isEqualToString:@"1"]) { [[backupsFileManager shareInstance] getDataWrongToChangeFailFun]; } } } else{ //异常 [self tryAgainApplyBackupsFun]; } } else{ //异常 [self tryAgainApplyBackupsFun]; } } #pragma mark 遇到异常时 重新上传 - (void)tryAgainApplyBackupsFun { self.backupsTaskUid = nil; [self ApplyBackupsFileFun]; } #pragma mark 图片上传 -(void)beginGotoBackupsImageFun:(NSNumber*)fileLengthNum{ self.curPhotosBackupsTaskMod.didUploadBytes = [fileLengthNum longLongValue]; self.curPhotosBackupsTaskMod.preDidUploadBytes = self.curPhotosBackupsTaskMod.didUploadBytes; self.backupsCutFileDataArr = [self backupsFileCutPartsBy:self.backupsAllFileData withDidUpLoadSize:[fileLengthNum integerValue]]; if(!self.backupsCutFileDataArr || self.backupsCutFileDataArr.count == 0){ [[backupsFileManager shareInstance] getDataWrongToChangeFailFun]; return; } //答复的taskUid 跟申请的一致 开发发送内容 [self backupsFileFun]; } #pragma mark 视频上传 //每次从文件重新切一片 传一片 -(void)backupsBeginGotoUploadVidelFun:(NSNumber*)fileLengthNum{ self.curPhotosBackupsTaskMod.didUploadBytes = [fileLengthNum longLongValue]; self.curPhotosBackupsTaskMod.preDidUploadBytes = self.curPhotosBackupsTaskMod.didUploadBytes; NSData *data = [self backupsCutVideoFileFunAtIndex:fileLengthNum.longLongValue]; HLog(@"data leng:%ld",[data length]); if([data length] == 0){ [self clearBackupsFileDataFun]; [[backupsFileManager shareInstance] getDataWrongToChangeFailFun]; HLog(@"hxd fail 备份任务异常终止") return; } self.backupsAllFileData = data; self.backupsCutFileDataArr = [self backupsFileCutPartsBy:self.backupsAllFileData withDidUpLoadSize:0]; self.backupsIndexOfUploadFlie = 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.backupsCutFileDataArr || self.backupsCutFileDataArr.count == 0){ [self clearBackupsFileDataFun]; [[backupsFileManager shareInstance] getDataWrongToChangeFailFun]; HLog(@"hxd fail 备份任务异常终止") return; } //答复的taskUid 跟申请的一致 开发发送内容 [self backupsFileFun]; } #pragma mark 分段读视频文件 -(NSData*)backupsCutVideoFileFunAtIndex:(NSUInteger)dataIndex{ NSString *filePath = [cachesFileManager getFilePathWithName:self.curPhotosBackupsTaskMod.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(dataIndex == 0 || self.curPhotosBackupsTaskMod.totalBytes == 0){ self.curPhotosBackupsTaskMod.totalBytes = endOfFile; } 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)backupsFileFunServiceResponseFun:(NSDictionary *)dataDict { //"type":"backUpFileRandomRet","data":{"progress":"0\/1","filePath":"/sdcard/DCIM/IMG_0889.JPG","fileLenth":258066,"status":"0","msg":"success"}} 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 ([status isEqualToString:@"1"]) { [[backupsFileManager shareInstance] getDataWrongToChangeFailFun]; }else if ([status isEqualToString:@"2"]){ NSString *tipStr = NSLocalizedString(@"disk_not_find_tip",nil); [[backupsFileManager shareInstance] changeBackupsFileStateToFailWith:tipStr]; mainBlock(^{ [[iToast makeText:tipStr] show]; }); } else if ([status isEqualToString:@"5"]){ NSString *tipStr = NSLocalizedString(@"disk_space_not_tip",nil); [[backupsFileManager shareInstance] changeBackupsFileStateToFailWith:tipStr]; mainBlock(^{ [[iToast makeText:tipStr] show]; }); } else if ([status isEqualToString:@"0"]){ NSNumber * fileLengthNum = @0; if ([[data allKeys] containsObject:@"fileLenth"]) { fileLengthNum = data[@"fileLenth"]; } if (fileLengthNum && fileLengthNum.longLongValue >= self.curPhotosBackupsTaskMod.totalBytes) {// HLog(@"备份完成"); NSString *commandStr = [RCCommandHelp uploadOrBackupsDoneBy:self.backupsTaskUid withFileName:self.backupsFileName withType:NO]; //[self.commandChannelManager rc_sendData:commandStr]; [[backupsFileManager shareInstance] backupsFileDoneFun]; } else{ //异常处理 if([fileLengthNum longValue] - self.curBackupsPostion > EachPieceSzie){ HLog(@"发现上传后返回的长度 出错"); if(self.curPhotosBackupsTaskMod.curUploadFileType == uploadFileTypeVideo) {//清除已经取出来的数据 后面会根据fileLengthNum 重新拿数据 [self.backupsCutFileDataArr removeAllObjects]; } else{ NSInteger curIndex = [fileLengthNum longValue] % EachPieceSzie; //把当前的上传位置指定已经上传的前一片,后续++ self.backupsIndexOfUploadFlie = curIndex -1; } } self.curBackupsPostion = [fileLengthNum longValue]; [self handleBackupsingFun:fileLengthNum]; } } } } else{ //异常 [self tryAgainApplyBackupsFun]; } } else{ //异常 [self tryAgainApplyBackupsFun]; } } #pragma mark 处理上传过程 - (void)handleBackupsingFun:(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.curPhotosBackupsTaskMod.isBackupsSuspendType = backupsSuspendByOther; self.curPhotosBackupsTaskMod.curBackupsState = backupsStateSuspend; [[backupsFileManager shareInstance] changeBackupsFileStateFun]; return; } } BOOL haveOpenBackups = [HWDataManager getBoolWithKey:stringKeyAddSn(Const_photo_backups_state)]; if(!haveOpenBackups){ self.curPhotosBackupsTaskMod.isBackupsSuspendType = backupsSuspendByOther; self.curPhotosBackupsTaskMod.curBackupsState = backupsStateSuspend; [[backupsFileManager shareInstance] changeBackupsFileStateFun]; return; } if(self.curPhotosBackupsTaskMod.isBackupsSuspendType != backupsSuspendNone){ [self clearBackupsFileDataFun]; return; } self.curPhotosBackupsTaskMod.didUploadBytes = [fileLengthNum longLongValue]; [[backupsFileManager shareInstance] changeBackupsFileStateFun]; if(self.backupsCutFileDataArr.count >0 && self.backupsIndexOfUploadFlie < (self.backupsCutFileDataArr.count -1)) { self.backupsIndexOfUploadFlie ++; [self backupsFileFun]; } else if(self.curPhotosBackupsTaskMod.curUploadFileType == uploadFileTypeVideo && fileLengthNum && fileLengthNum.longLongValue < self.curPhotosBackupsTaskMod.totalBytes){ //视频做了二次切割 不一定上传完了 HLog(@"视频上传传完一片 再来一片"); [self backupsBeginGotoUploadVidelFun:fileLengthNum]; } else{ HLog(@"备份完成"); NSString *commandStr = [RCCommandHelp uploadOrBackupsDoneBy:self.backupsTaskUid withFileName:self.backupsFileName withType:NO]; //[self.commandChannelManager rc_sendData:commandStr]; [[backupsFileManager shareInstance] backupsFileDoneFun]; } } -(void)clearBackupsFileDataFun{ //上传数据清空 self.backupsTaskUid = nil; self.backupsFileName = nil; self.backupsAllFileData = nil; self.backupsIndexOfUploadFlie = 0; self.backupsCutFileDataArr = nil; self.curPhotosBackupsTaskMod = nil; } - (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 *)backupsFileCutPartsBy:(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.backupsIndexOfUploadFlie = 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)); [dataArray addObject:[data subdataWithRange:range]]; 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)backupsFileFun { //HLog(@"上传文件%ld/%ld",self.indexOfUploadFlie +1,self.cutFileDataArr.count); if(self.backupsCutFileDataArr.count <= self.backupsIndexOfUploadFlie) { return; } //data NSData *curData = self.backupsCutFileDataArr[self.backupsIndexOfUploadFlie]; //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 = 16; unsigned char msgType = 21; NSData *msgTypeData = [NSMutableData dataWithBytes:&msgType length:sizeof(msgType)]; //HLog(@"msgTypeData:%@",msgTypeData); //taskId NSData *taskIdData = [self.backupsTaskUid 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.backupsIndexOfUploadFlie ;//+ 1; //NSData *curNumData = [NSData dataWithBytes:&curNum length:sizeof(curNum)]; NSData * curNumData = [self int2Data:curNum]; //HLog(@"curNumData:%@",curNumData); //totalNum int totalNum = (int)self.backupsCutFileDataArr.count; //NSData *totalNumData = [NSData dataWithBytes:&totalNum length:sizeof(totalNum)]; NSData * totalNumData = [self int2Data:totalNum]; //HLog(@"totalNumData:%@",totalNumData); long Postion = self.curBackupsPostion; 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); } @end