customDownloadManager.m 15 KB

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