nasMixUploadManager.m 41 KB

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