nasUploadManager.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. //
  2. // nasUploadManager.m
  3. // Private-X
  4. //
  5. // Created by xd h on 2024/8/7.
  6. //
  7. #import "nasUploadManager.h"
  8. #import "AFNetworking.h"
  9. #import "AFNetworkReachabilityManager.h"
  10. #import "AFHTTPSessionManager.h"
  11. #import "frpUploadModel.h"
  12. #define Kboundary @"Boundaryhxd"
  13. #define KNewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
  14. @interface nasUploadManager ()<NSURLSessionDataDelegate>
  15. @property(nonatomic,strong)AFHTTPSessionManager *uploadManager;
  16. @property (nonatomic, strong) NSURLSessionDataTask *uploadTask;
  17. @end
  18. @implementation nasUploadManager
  19. + (instancetype)shareInstance {
  20. static nasUploadManager *_instance;
  21. static dispatch_once_t onceToken;
  22. dispatch_once(&onceToken, ^{
  23. _instance = [[self alloc] init];
  24. });
  25. return _instance;
  26. }
  27. - (id)init
  28. {
  29. self = [super init];
  30. if (self) {
  31. [self initManager];
  32. }
  33. return self;
  34. }
  35. - (void)initManager
  36. {
  37. _uploadManager = [[AFHTTPSessionManager alloc] init];
  38. _uploadManager.requestSerializer = [AFJSONRequestSerializer serializer];
  39. _uploadManager.responseSerializer = [AFJSONResponseSerializer serializer];
  40. [_uploadManager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
  41. _uploadManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",@"text/json", @"text/javascript",@"text/html",@"text/plain",nil];
  42. _uploadManager.requestSerializer.timeoutInterval = 180;
  43. }
  44. #pragma mark NSURLSessionDataDelegate
  45. // 1响应
  46. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
  47. completionHandler(NSURLSessionResponseAllow);
  48. }
  49. // 上传进度
  50. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
  51. //每包发送的大小bytesSent,totalBytesSent已经上传了多少;totalBytesExpectedToSend总共要发送多少
  52. // 32768 = 32KB
  53. HLog(@"didSendBodyData: %lld--%lld-%lld", bytesSent, totalBytesSent, totalBytesExpectedToSend);
  54. }
  55. // 2 接收数据
  56. //- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
  57. //
  58. // NSDictionary *infoDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
  59. // HLog(@"%@", infoDict);
  60. //
  61. //}
  62. // 3 完成
  63. //- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
  64. // HLog(@"上传完成 task:%@---error:%@", task,error);
  65. //}
  66. - (NSMutableData *)getBodyDataWithRequest:(NSMutableURLRequest *)request withModel:(uploadFileDataModel*)dataModel withData:(NSData*)data withPara:(NSMutableDictionary*)params{
  67. //1 边界符号要配置请求头里面去
  68. /*
  69. multipart/form-data 是表单格式
  70. charset=utf-8 是utf-8编码
  71. bounary 是表单开头
  72. */
  73. [request setValue:[NSString stringWithFormat:@"multipart/form-data; charset=utf-8; boundary=%@", Kboundary] forHTTPHeaderField:@"Content-Type"];
  74. /// body
  75. NSMutableData *boydData = [NSMutableData data];
  76. // 2.1 边界符号(开始边界)
  77. //2.1.1 其它参数
  78. NSMutableString *paraString = [NSMutableString new];
  79. //[paraString appendFormat:@"--%@\r\n",Kboundary];//\n:换行 \n:切换到行首
  80. for (NSString *key in params) {
  81. NSString *value = params[key];
  82. [paraString appendFormat:@"--%@\r\n",Kboundary];//\n:换行 \n:切换到行首
  83. [paraString appendFormat:@"Content-Disposition: form-data; name=\"%@\"",key];
  84. [paraString appendFormat:@"\r\n"];
  85. [paraString appendFormat:@"\r\n"];
  86. [paraString appendFormat:@"%@\r\n",value];
  87. }
  88. //[boydData appendData:[paraString dataUsingEncoding:NSUTF8StringEncoding]];
  89. // body每一个段内容以换行符作为结束标示
  90. NSString *fileBeginBoundary = [NSString stringWithFormat:@"--%@\r\n", Kboundary];
  91. //[boydData appendData:[fileBeginBoundary dataUsingEncoding:NSUTF8StringEncoding]];
  92. [paraString appendString:fileBeginBoundary];
  93. // 2.2 属性配置 名字;key;类型
  94. NSString *serverFileKey = @"image"; //key
  95. //NSString *serverFileKey = @"file";
  96. NSString *serverContentTypes = @"image/png"; //类型
  97. if (dataModel.curUploadFileType == uploadFileTypeVideo) {
  98. serverFileKey = @"video";
  99. serverContentTypes = @"video/mp4";
  100. }
  101. NSString *serverFileName = dataModel.filename; //name
  102. // filename已命名文件; name相当于一个key, 这个名字和服务器保持一致
  103. /*
  104. 理解key,表单发送给服务端,服务端拿到数据之后,可以将任务解析成一个字典了imageDict;图片数据会通过这个字典里面的name来获取图片(伪代码 image = imageDict[serverFileKey])
  105. */
  106. //2.3 拼接数据(创建一个字符串来拼装)
  107. NSMutableString *string = [NSMutableString new];
  108. [string appendFormat:@"Content-Disposition:form-data; name=\"%@\"; filename=\"%@\" ", @"file", serverFileName];
  109. //[string appendFormat:@"%@", KNewLine];
  110. [string appendFormat:@"\r\n"];
  111. [string appendFormat:@"Content-Type:%@", serverContentTypes];
  112. // [string appendFormat:@"%@", KNewLine];
  113. // [string appendFormat:@"%@", KNewLine];
  114. [string appendFormat:@"\r\n"];
  115. [string appendFormat:@"\r\n"];
  116. // [boydData appendData:[string dataUsingEncoding:NSUTF8StringEncoding]];
  117. [paraString appendString:string];
  118. [boydData appendData:[paraString dataUsingEncoding:NSUTF8StringEncoding]];
  119. // 2.3 拼接数据(拼接文件数据)
  120. [boydData appendData:data];
  121. // 2.4 边界符号 (结束边界)
  122. NSString *fileEndBoundary = [NSString stringWithFormat:@"\r\n--%@--", Kboundary];
  123. [boydData appendData:[fileEndBoundary dataUsingEncoding:NSUTF8StringEncoding]];
  124. return boydData;
  125. }
  126. - (void)startUpload:(NSMutableDictionary *)params model:(uploadFileDataModel*)dataModel data:(NSData *)data success:(netWork_Success)success faild:(netWork_Faild)faildStr {
  127. NSString *urlString = ksharedAppDelegate.NASFileByBoxService;
  128. urlString = [[NSString alloc] initWithFormat:@"%@uploadFile",urlString];
  129. NSURL *URL = [NSURL URLWithString:urlString];
  130. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
  131. [request setHTTPMethod:@"POST"];
  132. //请求体
  133. NSMutableData *bodyData = [self getBodyDataWithRequest:request withModel:dataModel withData:data withPara:params];
  134. //设置请求体
  135. [request setHTTPMethod:@"POST"];
  136. [request setHTTPBody:bodyData];
  137. //设置请求体长度
  138. NSInteger length = [bodyData length];
  139. [request setValue:[NSString stringWithFormat:@"%ld",length] forHTTPHeaderField:@"Content-Length"];
  140. //设置 POST请求文件上传
  141. [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",Kboundary] forHTTPHeaderField:@"Content-Type"];
  142. KWeakSelf
  143. //回话对象
  144. NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
  145. //请求task
  146. /*
  147. 第一个参数:请求对象
  148. 第二个参数:传递是要上传的数据(请求体)
  149. 第三个参数:
  150. */
  151. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:nil completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  152. //解析
  153. [weakSelf handleCustomUploadResultBy:data withResponse:response withError:error success:success faild:faildStr];
  154. }];
  155. // NSString *filePath = [cachesFileManager getFilePathWithName:dataModel.filename type:uploadFileTypeVideo]; // 文件路径
  156. // NSURL *filePathUrl = [NSURL URLWithString:filePath];
  157. //
  158. // NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:filePathUrl completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  159. // HLog(@"data string:%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
  160. //[weakSelf handleCustomUploadResultBy:data withResponse:response withError:error];
  161. // }];
  162. // NSURLSessionDataTask *uploadTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  163. //
  164. // HLog(@"data string:%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
  165. //[weakSelf handleCustomUploadResultBy:data withResponse:response withError:error];
  166. //
  167. //// NSJSONSerialization *object = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
  168. //// NSDictionary *dict = (NSDictionary *)object;
  169. //// NSLog(@"=====%@",[dict objectForKey:@"success"]);
  170. // }];
  171. //执行Task
  172. [uploadTask resume];
  173. self.uploadTask = uploadTask;
  174. }
  175. #pragma mark 用系统方法写的上传处理
  176. - (void)handleCustomUploadResultBy:(NSData*)data withResponse:(NSURLResponse*)response withError:(NSError*)error success:(netWork_Success)success faild:(netWork_Faild)faildStr{
  177. if(error){
  178. HLog(@"上传错误:%@",error)
  179. faildStr(error);
  180. return;
  181. }
  182. NSJSONSerialization *object = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
  183. NSDictionary *dict = (NSDictionary *)object;
  184. frpUploadModel *model = [[frpUploadModel alloc] initWithDictionary:dict error:nil];
  185. if(model && [model.msg isEqualToString:@"success"]){
  186. success(dict);
  187. }
  188. else{
  189. NSError *err = [NSError new];
  190. faildStr(err);
  191. }
  192. }
  193. #pragma mark afnetwork
  194. - (void)nasUploadFileToFileServiceWithParams:(NSMutableDictionary *)params model:(uploadFileDataModel*)dataModel data:(NSData *)data success:(netWork_Success)success faild:(netWork_Faild)faildStr {
  195. //test code
  196. // [self startUpload:params model:dataModel data:data success:success faild:faildStr];
  197. // return;
  198. NSString *urlString = ksharedAppDelegate.NASFileByBoxService;
  199. urlString = [[NSString alloc] initWithFormat:@"%@uploadFile",urlString];
  200. NSInteger position = 0;
  201. if([[params allKeys] containsObject:@"position"]){
  202. NSNumber *positionNumber = params[@"position"];
  203. position = positionNumber.longValue;
  204. }
  205. __block long dataLength = [data length];
  206. HLog(@"上传地址:%@---%@---position:%ld--data 长度:%ld ",urlString,params,position,[data length]);
  207. self.uploadTask = [_uploadManager POST:urlString parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
  208. NSString *mimeType =@"application/octet-stream";
  209. if ([[params allKeys] containsObject:@"imageType"]) {
  210. mimeType = @"image/jpeg";
  211. //mimeType = @"jpg";
  212. }
  213. else if ([[params allKeys] containsObject:@"videoType"]) {
  214. //mimeType = @"video/mp4";
  215. mimeType = @"video";
  216. }
  217. [formData appendPartWithFileData:data name:@"file" fileName:params[@"filename"] mimeType:mimeType];
  218. } progress:^(NSProgress * _Nonnull uploadProgress) {
  219. HLog(@"上传 Progress:--%@---%lld",uploadProgress,uploadProgress.completedUnitCount)
  220. //dataModel.didUploadBytes = position + (uploadProgress.completedUnitCount);
  221. long didSendData = (long)(uploadProgress.fractionCompleted *dataLength);
  222. if(uploadProgress.fractionCompleted==1){
  223. didSendData = dataLength;
  224. }
  225. dataModel.didUploadBytes = position + didSendData;
  226. [[NSNotificationCenter defaultCenter] postNotificationName:uploadFileRefreshNotification object:dataModel];
  227. } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  228. success(responseObject);
  229. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error, id _Nonnull responseObject) {
  230. if(error.code != -999){
  231. faildStr(error);
  232. }
  233. }];
  234. data = nil;
  235. }
  236. - (void)beginUploadDataBy:(uploadFileDataModel*)dataModel success:(netWork_Success)success faild:(netWork_Faild)faildStr
  237. {
  238. NSMutableDictionary *paraDict = [NSMutableDictionary new];
  239. NSString* taskUid = dataModel.taskId;
  240. if(!taskUid || taskUid.length == 0){
  241. taskUid = [iTools getTaskUidStr];
  242. }
  243. [paraDict setObject:taskUid forKey:@"taskId"];
  244. [paraDict setObject:@0 forKey:@"position"];
  245. [paraDict setObject:@"true" forKey:@"isLast"];
  246. if(dataModel.savePath){
  247. [paraDict setObject:dataModel.savePath forKey:@"savePath"];
  248. }
  249. else{
  250. HLog(@"获取保存路径失败")
  251. return;
  252. }
  253. if(dataModel.filename){
  254. [paraDict setObject:dataModel.filename forKey:@"filename"];
  255. }
  256. else{
  257. HLog(@"获取用户名失败")
  258. return;
  259. }
  260. KWeakSelf
  261. if(dataModel.curUploadFileType == uploadFileTypeImage){
  262. [paraDict setObject:@1 forKey:@"imageType"];
  263. NSData *curData = dataModel.imageData;
  264. [self nasUploadFileToFileServiceWithParams:paraDict model:dataModel data:curData success:^(id _Nonnull responseObject) {
  265. HLog(@"%@上传完成 000",dataModel.filename)
  266. success(responseObject);
  267. } faild:^(NSError * _Nonnull error) {
  268. HLog(@"%@上传失败",dataModel.filename)
  269. faildStr(error);
  270. }];
  271. }
  272. else{
  273. [paraDict setObject:@0 forKey:@"videoType"];
  274. [paraDict setObject:@"false" forKey:@"isLast"];
  275. long curPosition = dataModel.didUploadBytes;
  276. [self beginUploadVideoDataFunBy:dataModel with:curPosition withPara:paraDict success:^(id _Nonnull responseObject) {
  277. success(responseObject);
  278. } faild:^(NSError * _Nonnull error) {
  279. faildStr(error);
  280. }];
  281. }
  282. }
  283. - (void)beginUploadVideoDataFunBy:(uploadFileDataModel*)dataModel with:(NSInteger)position withPara:(NSMutableDictionary*)paraDict success:(netWork_Success)success faild:(netWork_Faild)faildStr
  284. {
  285. BOOL isLastPicece = NO;
  286. if((dataModel.totalBytes - position) <= MaxNasUploadPieceSzie){
  287. [paraDict setObject:@"true" forKey:@"isLast"];
  288. isLastPicece = YES;
  289. }
  290. [paraDict setObject:[NSNumber numberWithLong:position] forKey:@"position"];
  291. //视频数据切片
  292. __block NSData *videoData = [self cutVideoFileFunAtIndex:position withMaxLenght:MaxNasUploadPieceSzie withModel:dataModel];
  293. KWeakSelf
  294. [self nasUploadFileToFileServiceWithParams:paraDict model:dataModel data:videoData success:^(id _Nonnull responseObject) {
  295. HLog(@"%@上传完成 222 %@",dataModel.filename,responseObject)
  296. videoData = nil;
  297. frpUploadModel *model = [[frpUploadModel alloc] initWithDictionary:responseObject error:nil];
  298. if(model && model.msg){
  299. if (isLastPicece) {
  300. success(responseObject);
  301. }
  302. else{
  303. [weakSelf beginUploadVideoDataFunBy:dataModel with:model.position withPara:paraDict success:^(id _Nonnull responseObject) {
  304. success(responseObject);
  305. } faild:^(NSError * _Nonnull error) {
  306. NSError *err = error;
  307. if(error.code != -999){
  308. faildStr(err);
  309. }
  310. }];
  311. }
  312. }
  313. else{
  314. videoData = nil;
  315. NSError *err = [NSError new];
  316. faildStr(err);
  317. }
  318. } faild:^(NSError * _Nonnull error) {
  319. HLog(@"%@上传失败---%@",dataModel.filename,error)
  320. videoData = nil;
  321. faildStr(error);
  322. }];
  323. }
  324. #pragma mark 分段读视频文件
  325. -(NSData*)cutVideoFileFunAtIndex:(NSUInteger)dataIndex withMaxLenght:(NSInteger)maxLengt withModel:(uploadFileDataModel*)dataModel{
  326. NSString *filePath = [cachesFileManager getFilePathWithName:dataModel.filename type:uploadFileTypeVideo]; // 文件路径
  327. NSFileManager *manager0 = [NSFileManager defaultManager];
  328. if(![manager0 fileExistsAtPath:filePath]) {
  329. return [NSData new];
  330. }
  331. NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath]; // 创建文件句柄
  332. // 设置分段读取的大小,这里以每次读取1KB为例
  333. const NSUInteger chunkSize = maxLengt;//cutVideoPieceSzie;//5 * 1024 *1024;
  334. NSMutableData *data = [NSMutableData data];
  335. //把视频切成两份文件给凌云
  336. // NSData* chunk = [fileHandle readDataOfLength:MaxNasUploadPieceSzie];
  337. //
  338. // [cachesFileManager getFileNameWithContent:chunk fileName:[[NSString alloc] initWithFormat:@"1_%@",dataModel.filename] type:uploadFileTypeVideo];
  339. //
  340. // [fileHandle seekToFileOffset:MaxNasUploadPieceSzie];
  341. // NSData* chunk2 = [fileHandle readDataToEndOfFile];
  342. // [cachesFileManager getFileNameWithContent:chunk2 fileName:[[NSString alloc] initWithFormat:@"2_%@",dataModel.filename] type:uploadFileTypeVideo];
  343. //
  344. // return chunk2;
  345. if (fileHandle) {
  346. long long endOfFile = [fileHandle seekToEndOfFile];
  347. if(dataModel.totalBytes == 0
  348. || dataModel.totalBytes < endOfFile){//异常处理
  349. dataModel.totalBytes = endOfFile;
  350. }
  351. //异常处理
  352. if(endOfFile == dataIndex){
  353. dataModel.totalBytes = endOfFile;
  354. dataModel.didUploadBytes = endOfFile;
  355. dataModel.curUploadStateType = uploadStateDone;
  356. [fileHandle closeFile];
  357. return data;
  358. }
  359. if (endOfFile >= chunkSize) {
  360. // 读取文件的分段数据到某个位置
  361. [fileHandle seekToFileOffset:dataIndex];
  362. // 读取文件的分段数据
  363. NSData* chunk = [fileHandle readDataOfLength:chunkSize];
  364. if (chunk) {
  365. [data appendData:chunk];
  366. }
  367. }
  368. else{
  369. // 读取文件的分段数据到某个位置
  370. [fileHandle seekToFileOffset:dataIndex];
  371. [data appendData:[fileHandle readDataToEndOfFile]];
  372. }
  373. // 在这里可以对文件内容进行处理
  374. // ...
  375. // 关闭文件句柄
  376. [fileHandle closeFile];
  377. }
  378. HLog(@"视频切片完成 dataIndex:%ld --长度:%ld",dataIndex,[data length])
  379. return data;
  380. }
  381. #pragma mark 取消任务
  382. - (void)cancelUploadTaskFun
  383. {
  384. [self.uploadTask cancel];
  385. }
  386. @end