customDownloadManager.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. //
  2. // customDownloadManager.m
  3. // 双子星云手机
  4. //
  5. // Created by xd h on 2024/7/1.
  6. //
  7. #import "customDownloadManager.h"
  8. #import "customDownloadOperation.h"
  9. #import "customDownloadCacheManager.h"
  10. @interface customDownloadManager ()<NSURLSessionDataDelegate>
  11. //排队等候下载的下载地址数组
  12. @property(nonatomic,strong) NSMutableArray *downloadWaitingUrlArr;
  13. //正在下载的下载地址数组
  14. @property(nonatomic,strong) NSMutableArray *downloadingOperationArr;
  15. @end
  16. @implementation customDownloadManager
  17. + (instancetype)shareManager {
  18. static customDownloadManager *_instance;
  19. static dispatch_once_t onceToken;
  20. dispatch_once(&onceToken, ^{
  21. _instance = [[self alloc] init];
  22. });
  23. return _instance;
  24. }
  25. - (instancetype)init {
  26. if (self = [super init]) {
  27. _maxDownLoadCount = 1;
  28. //[self registeNotification];
  29. }
  30. return self;
  31. }
  32. - (NSString*)uid{
  33. if(!_uid || _uid.length == 0){
  34. return @"customUserName";
  35. }
  36. return _uid;
  37. }
  38. /** 添加要下载的 网络连接 */
  39. - (void)addDownloadWithURLs:(NSArray *)urls{
  40. for (NSString *addUrl in urls) {
  41. BOOL needAddType = YES;
  42. //1. 排查下载中
  43. for (customDownloadOperation *operationDoing in self.downloadingOperationArr) {
  44. if([operationDoing.url isEqualToString:addUrl]){
  45. needAddType = NO;
  46. break;
  47. }
  48. }
  49. //1. 排查等待下载
  50. for (NSString *waitUrl in self.downloadWaitingUrlArr) {
  51. if([waitUrl isEqualToString:addUrl]){
  52. needAddType = NO;
  53. break;
  54. }
  55. }
  56. if(needAddType){
  57. [self.downloadWaitingUrlArr addObject:addUrl];
  58. }
  59. }
  60. //启动下载
  61. [self beginDownload];
  62. }
  63. //在添加下载地址后 启动下载
  64. - (void)beginDownload
  65. {
  66. @synchronized (self) {
  67. if(self.downloadingOperationArr.count == _maxDownLoadCount){
  68. HLog(@"正在下载的数量达到了最大值 %ld",_maxDownLoadCount)
  69. return;
  70. }
  71. if(self.downloadWaitingUrlArr.count == 0){
  72. HLog(@"没有等待中的下载任务")
  73. return;
  74. }
  75. NSInteger canAddTaskNumber = _maxDownLoadCount - self.downloadingOperationArr.count;
  76. for (int i=0; i<canAddTaskNumber; i++) {
  77. if(self.downloadWaitingUrlArr.count >= 1){
  78. //创建下载任务
  79. NSString *downloadUrl = self.downloadWaitingUrlArr.firstObject;
  80. NSURLSession *session = [self creatNewSessionFun];
  81. customDownloadOperation * operation = [[customDownloadOperation alloc] initWith:downloadUrl session:session];
  82. [operation.dataTask resume];
  83. //等待下载中的任务
  84. [self.downloadWaitingUrlArr removeObjectAtIndex:0];
  85. //添加到下载中数组
  86. [self.downloadingOperationArr addObject:operation];
  87. }
  88. }
  89. }
  90. }
  91. /** 开始任务(不会自动添加任务,列队中没有就直接返回) 后续改为会自动添加任务 */
  92. - (void)startDownLoadWithUrl:(NSString *)url
  93. {
  94. BOOL needAddType = YES;
  95. //1. 排查下载中
  96. for (customDownloadOperation *operationDoing in self.downloadingOperationArr) {
  97. if([operationDoing.url isEqualToString:url]){
  98. needAddType = NO;
  99. break;
  100. }
  101. }
  102. //1. 排查等待下载
  103. for (NSString *waitUrl in self.downloadWaitingUrlArr) {
  104. if([waitUrl isEqualToString:url]){
  105. needAddType = NO;
  106. break;
  107. }
  108. }
  109. if(needAddType){
  110. [self.downloadWaitingUrlArr addObject:url];
  111. }
  112. //启动下载
  113. [self beginDownload];
  114. }
  115. /** 删除任务(删除下载url内容的任务) */
  116. - (void)deleteDownloadWithUrl:(NSString *)url
  117. {
  118. //1.检测等待下载的任务
  119. for (NSString*waitingUrl in self.downloadWaitingUrlArr) {
  120. if ([waitingUrl isEqualToString:url]) {
  121. [self.downloadWaitingUrlArr removeObject:waitingUrl];
  122. break;
  123. }
  124. }
  125. //2.检测下载中的任务
  126. for (customDownloadOperation *operationDoing in self.downloadingOperationArr) {
  127. if([operationDoing.url isEqualToString:url]){
  128. [operationDoing.dataTask cancel];
  129. // operationDoing.dataTask = nil;
  130. // operationDoing.session = nil;
  131. //
  132. // [operationDoing.handle closeFile];
  133. // operationDoing.handle = nil;
  134. //
  135. // [self.downloadingOperationArr removeObject:operationDoing];
  136. break;
  137. }
  138. }
  139. //3. 删除本地文件
  140. [customDownloadCacheManager deleteFileWithUrl:url];
  141. //4.进行下一个任务
  142. //[self beginDownload];
  143. }
  144. /** 暂停任务(暂停下载url内容的任务) */
  145. - (void)supendDownloadWithUrl:(NSString *)url
  146. {
  147. //1.检测等待下载的任务
  148. for (NSString*waitingUrl in self.downloadWaitingUrlArr) {
  149. if ([waitingUrl isEqualToString:url]) {
  150. [self.downloadWaitingUrlArr removeObject:waitingUrl];
  151. break;
  152. }
  153. }
  154. //2.检测下载中的任务
  155. for (customDownloadOperation *operationDoing in self.downloadingOperationArr) {
  156. if([operationDoing.url isEqualToString:url]){
  157. operationDoing.isManualCancel = YES;
  158. if(operationDoing.dataTask){
  159. [operationDoing.dataTask cancel];
  160. }
  161. // operationDoing.dataTask = nil;
  162. // operationDoing.session = nil;
  163. //
  164. // [operationDoing.handle closeFile];
  165. // operationDoing.handle = nil;
  166. //
  167. // //删除后 didCompleteWithError 不再处理
  168. // [self.downloadingOperationArr removeObject:operationDoing];
  169. break;
  170. }
  171. }
  172. //进行下一个任务
  173. //[self beginDownload];
  174. }
  175. /** 暂停当前所有的下载任务 下载任务不会从列队中删除 */
  176. - (void)suspendAllDownloadTask
  177. {
  178. //1.删除等待下载的任务
  179. [self.downloadWaitingUrlArr removeAllObjects];
  180. //2.检测下载中的任务
  181. for (customDownloadOperation *operationDoing in self.downloadingOperationArr) {
  182. operationDoing.isManualCancel = YES;
  183. if(operationDoing.dataTask){
  184. [operationDoing.dataTask cancel];
  185. }
  186. operationDoing.dataTask = nil;
  187. operationDoing.session = nil;
  188. [operationDoing.handle closeFile];
  189. operationDoing.handle = nil;
  190. [self.downloadingOperationArr removeObject:operationDoing];
  191. }
  192. }
  193. #pragma mark - <NSURLSessionDataDelegate>
  194. // ssl 服务 证书信任
  195. - (void)URLSession:(NSURLSession *)session
  196. didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
  197. completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{
  198. if(![challenge.protectionSpace.authenticationMethod isEqualToString:@"NSURLAuthenticationMethodServerTrust"]) {
  199. return;
  200. }
  201. // 信任该插件
  202. NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust];
  203. // 第一个参数 告诉系统如何处置
  204. completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
  205. }
  206. //当请求协议是https的时候回调用该方法
  207. //Challenge 挑战 质询(受保护空间)
  208. //NSURLAuthenticationMethodServerTrust 服务器信任证书
  209. - (void)URLSession:(NSURLSession *)session
  210. task:(NSURLSessionTask *)task
  211. didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
  212. completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler {
  213. if(![challenge.protectionSpace.authenticationMethod isEqualToString:@"NSURLAuthenticationMethodServerTrust"]) {
  214. return;
  215. }
  216. // 信任该插件
  217. NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust];
  218. // 第一个参数 告诉系统如何处置
  219. completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
  220. }
  221. // 接受到响应调用
  222. - (void)URLSession:(NSURLSession *)session
  223. dataTask:(NSURLSessionDataTask *)dataTask
  224. didReceiveResponse:(NSURLResponse *)response
  225. completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
  226. // 将响应交给列队处理
  227. [self handleOperateby:dataTask WithResponse:response];
  228. // 允许下载
  229. completionHandler(NSURLSessionResponseAllow);
  230. }
  231. // 接受到数据碎片 的时候调用,调用多次
  232. - (void)URLSession:(NSURLSession *)session
  233. dataTask:(NSURLSessionDataTask *)dataTask
  234. didReceiveData:(NSData *)data {
  235. // 接收到session 下载碎片交个列队管理
  236. [self dataTask:dataTask didReceiveData:data];
  237. }
  238. // <NSURLSessionDataDelegate> 完成下载
  239. - (void)URLSession:(NSURLSession *)session
  240. task:(NSURLSessionTask *)task
  241. didCompleteWithError:(nullable NSError *)error {
  242. [self task:task didCompleteWithError:error];
  243. }
  244. - (void)URLSession:(NSURLSession *)session
  245. task:(NSURLSessionTask *)task
  246. needNewBodyStream:(void (^)(NSInputStream * _Nullable bodyStream))completionHandler {
  247. }
  248. - (void)URLSession:(NSURLSession *)session
  249. task:(NSURLSessionTask *)task
  250. didSendBodyData:(int64_t)bytesSent
  251. totalBytesSent:(int64_t)totalBytesSent
  252. totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
  253. }
  254. #pragma mark 处理接收到的数据
  255. // 接收到相应时
  256. - (void)handleOperateby:(NSURLSessionTask*)dataTask WithResponse:(NSURLResponse *)response {
  257. customDownloadOperation *operation = nil;
  258. for (customDownloadOperation *operationDoing in self.downloadingOperationArr) {
  259. if(operationDoing.dataTask == dataTask){
  260. operation = operationDoing;
  261. break;
  262. }
  263. }
  264. if (!operation) {
  265. HLog(@"没找到当前下载任务");
  266. [dataTask cancel];
  267. return;
  268. }
  269. // 总的size
  270. if (operation.currentSize + response.expectedContentLength == 0) {
  271. HLog(@"下载数据回调异常");
  272. return;
  273. }
  274. operation.totalSize = operation.currentSize + response.expectedContentLength;
  275. // 创建空的文件夹
  276. if (operation.currentSize == 0) {
  277. // 创建空的文件
  278. [[NSFileManager defaultManager] createFileAtPath:operation.fullPath contents:nil attributes:nil];
  279. }
  280. // 创建文件句柄
  281. operation.handle = [NSFileHandle fileHandleForWritingAtPath:operation.fullPath];
  282. // 文件句柄移动到文件末尾 位置 // 返回值是 unsign long long
  283. [operation.handle seekToEndOfFile];
  284. // 开始下载记录文件下载信息
  285. operation.downloadState = customDownloadStateDoing;
  286. // 回调给外界
  287. }
  288. - (void)dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
  289. {
  290. customDownloadOperation *operation = nil;
  291. for (customDownloadOperation *operationDoing in self.downloadingOperationArr) {
  292. if(operationDoing.dataTask == dataTask){
  293. operation = operationDoing;
  294. break;
  295. }
  296. }
  297. if (!operation) {
  298. HLog(@"没找到当前下载任务");
  299. [dataTask cancel];
  300. return;
  301. }
  302. // 获得已经下载的文件大小
  303. operation.currentSize += data.length;
  304. HLog(@"%@\n currentSize:%lld---progress:%.2f",operation.url, operation.currentSize, 1.00*operation.currentSize/operation.totalSize);
  305. // 写入文件
  306. if(operation.handle){
  307. [operation.handle writeData:data];
  308. }
  309. // 下载中通知
  310. [self operationDoningWithOperation:operation];
  311. }
  312. - (void)task:(NSURLSessionTask *)dataTask didCompleteWithError:(NSError *)error
  313. {
  314. customDownloadOperation *operation = nil;
  315. for (customDownloadOperation *operationDoing in self.downloadingOperationArr) {
  316. if(operationDoing.dataTask == dataTask){
  317. operation = operationDoing;
  318. [self.downloadingOperationArr removeObject:operationDoing];
  319. break;
  320. }
  321. }
  322. if (!operation) {
  323. HLog(@"没找到当前下载任务");
  324. //[dataTask suspend];
  325. return;
  326. }
  327. if(operation.dataTask){
  328. //[operation.dataTask cancel];
  329. operation.dataTask = nil;
  330. operation.session = nil;
  331. // 关闭文件句柄
  332. [operation.handle closeFile];
  333. // 释放文件句柄
  334. operation.handle = nil;
  335. }
  336. if(operation.isManualCancel){
  337. [self beginDownload];
  338. return;
  339. }
  340. // 完成下载 通知 block
  341. if (error) {
  342. [self operationFailedWithOperation:operation];
  343. } else {
  344. operation.isFinished = YES;
  345. operation.timeStamp = [iTools getNowTimeStamp];
  346. [self operationSuccessWithOperation:operation];
  347. }
  348. [self beginDownload];
  349. }
  350. #pragma mark 发送通知
  351. // 失败某一个operation 保存本地 通知外界
  352. - (void)operationFailedWithOperation:(customDownloadOperation *)operation {
  353. HLog(@"DownloadTaskExeError \n %@",operation.url);
  354. operation.downloadState = customDownloadStateFailed;
  355. [[NSNotificationCenter defaultCenter] postNotificationName:customDownloadTaskExeError object:nil userInfo:@{@"operation" : operation}];
  356. }
  357. //外面的下载数据跳的太频繁 控制下
  358. static int DownloadTaskExeingNotNum = 0;
  359. // 重启某一个operation 保存本地 通知外界
  360. - (void)operationDoningWithOperation:(customDownloadOperation *)operation {
  361. HLog(@"DownloadTaskExeing");
  362. DownloadTaskExeingNotNum ++;
  363. if (DownloadTaskExeingNotNum >= 10) {
  364. DownloadTaskExeingNotNum = 0;
  365. [[NSNotificationCenter defaultCenter] postNotificationName:customDownloadTaskExeing object:nil userInfo:@{@"operation" : operation}];
  366. }
  367. }
  368. // 等待某一个operation 保存本地 通知外界
  369. - (void)operationSuccessWithOperation:(customDownloadOperation *)operation {
  370. HLog(@"DownloadTaskExeEnd \n %@",operation.url);
  371. operation.downloadState = customDownloadStateCompleted;
  372. [[NSNotificationCenter defaultCenter] postNotificationName:customDownloadTaskExeEnd object:nil userInfo:@{@"operation" : operation}];
  373. }
  374. -(NSURLSession*)creatNewSessionFun
  375. {
  376. NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
  377. // 设置请求超时
  378. config.timeoutIntervalForRequest = -1;
  379. // config.networkServiceType = NSURLNetworkServiceTypeVideo;
  380. config.timeoutIntervalForResource = -1;
  381. // config.TLSMaximumSupportedProtocol = kSSLProtocolAll;
  382. //_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue currentQueue]];
  383. NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
  384. return session;
  385. }
  386. #pragma mark - lazy load
  387. - (NSMutableArray *)downloadWaitingUrlArr {
  388. if (!_downloadWaitingUrlArr) {
  389. _downloadWaitingUrlArr = [NSMutableArray array];
  390. }
  391. return _downloadWaitingUrlArr;
  392. }
  393. - (NSMutableArray *)downloadingOperationArr {
  394. if (!_downloadingOperationArr) {
  395. _downloadingOperationArr = [NSMutableArray array];
  396. }
  397. return _downloadingOperationArr;
  398. }
  399. @end