123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662 |
- //
- // nasDownloadManager.m
- // Private-X
- //
- // Created by xd h on 2024/8/5.
- //
- #import "nasDownloadManager.h"
- #import "customDownloadOperation.h"
- #import "customDownloadCacheManager.h"
- @interface nasDownloadManager ()<NSURLSessionDataDelegate>
- //排队等候下载的下载地址数组
- @property(nonatomic,strong) NSMutableArray *downloadWaitingUrlArr;
- //正在下载的下载地址数组
- @property(nonatomic,strong) NSMutableArray *downloadingOperationArr;
- @property (nonatomic, strong) NSLock *lock;
- @end
- @implementation nasDownloadManager
- + (instancetype)shareManager {
- static nasDownloadManager *_instance;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- _instance = [[self alloc] init];
- });
- return _instance;
- }
- - (instancetype)init {
- if (self = [super init]) {
- _maxDownLoadCount = 1;
- _lock = [[NSLock alloc] init];
- //[self registeNotification];
- }
- return self;
- }
- - (NSString*)uid{
- if(!_uid || _uid.length == 0){
- return @"customUserName";
- }
-
- return _uid;
- }
- /** 添加要下载的 网络连接 */
- - (void)addDownloadWithURLs:(NSArray *)urls{
- HLog(@"addDownloadWithURLs")
- NSMutableArray *curDownloadingOperationArr = [NSMutableArray arrayWithArray:self.downloadingOperationArr];
-
- NSMutableArray *curWaitingUrlArr = [NSMutableArray arrayWithArray:self.downloadWaitingUrlArr];
-
- for (NSString *addUrl in urls) {
- BOOL needAddType = YES;
-
- //1. 排查下载中
- for (customDownloadOperation *operationDoing in curDownloadingOperationArr) {
- if([operationDoing.url isEqualToString:addUrl]){
- needAddType = NO;
- break;
- }
- }
-
- //1. 排查等待下载
- for (NSString *waitUrl in curWaitingUrlArr) {
- if([waitUrl isEqualToString:addUrl]){
- needAddType = NO;
- break;
- }
- }
-
- if(needAddType){
- [self.downloadWaitingUrlArr addObject:addUrl];
- }
- }
-
- //HLog(@"beginDownload downloadWaitingUrlArr:%@ --- %ld -- \n downloadingOperationArr:%@ ----%ld --%ld --- urls:%@",self.downloadWaitingUrlArr,self.downloadWaitingUrlArr.count,self.downloadingOperationArr,self.downloadingOperationArr.count,_maxDownLoadCount,urls)
-
- //启动下载
- [self beginDownload];
- }
- //在添加下载地址后 启动下载
- - (void)beginDownload
- {
- @synchronized (self) {
- //HLog(@"beginDownload downloadWaitingUrlArr:%@ --- %ld -- \n downloadingOperationArr:%@ ----%ld --%ld",self.downloadWaitingUrlArr,self.downloadWaitingUrlArr.count,self.downloadingOperationArr,self.downloadingOperationArr.count,_maxDownLoadCount)
-
- if(self.downloadingOperationArr.count == _maxDownLoadCount){
- HLog(@"正在下载的数量达到了最大值 %ld",_maxDownLoadCount)
- return;
- }
-
- if(self.downloadWaitingUrlArr.count == 0){
- HLog(@"没有等待中的下载任务")
- return;
- }
-
- //判断手机磁盘空间是否有剩余
- if(![iTools checkFreeDiskSpaceInBytesIsOK:500*1024*1024]){
- mainBlock(^{
- [[iToast makeText:NSLocalizedString(@"phone_space_Insufficient",nil)] show];
- });
-
- return;
- }
-
- NSInteger canAddTaskNumber = _maxDownLoadCount - self.downloadingOperationArr.count;
-
- for (int i=0; i<canAddTaskNumber; i++) {
-
- if(self.downloadWaitingUrlArr.count >= 1){
- //创建下载任务
- NSString *downloadUrl = self.downloadWaitingUrlArr.firstObject;
-
- NSURLSession *session = [self creatNewSessionFun];
- customDownloadOperation * operation = [[customDownloadOperation alloc] initWith:downloadUrl session:session];
- [operation.dataTask resume];
-
- //等待下载中的任务
- [self.downloadWaitingUrlArr removeObjectAtIndex:0];
- //添加到下载中数组
- [self.downloadingOperationArr addObject:operation];
- }
-
- }
-
- }
-
- }
- /** 开始任务(不会自动添加任务,列队中没有就直接返回) 后续改为会自动添加任务 */
- - (void)startDownLoadWithUrl:(NSString *)url
- {
-
- BOOL needAddType = YES;
- HLog(@"startDownLoadWithUrl")
- NSMutableArray *curDownloadingOperationArr = [NSMutableArray arrayWithArray:self.downloadingOperationArr];
-
- NSMutableArray *curWaitingUrlArr = [NSMutableArray arrayWithArray:self.downloadWaitingUrlArr];
-
-
- //1. 排查下载中
- for (customDownloadOperation *operationDoing in curDownloadingOperationArr) {
- if([operationDoing.url isEqualToString:url]){
- needAddType = NO;
- break;
- }
- }
-
- //1. 排查等待下载
- for (NSString *waitUrl in curWaitingUrlArr) {
- if([waitUrl isEqualToString:url]){
- needAddType = NO;
- break;
- }
- }
-
- if(needAddType){
- [self.downloadWaitingUrlArr addObject:url];
- }
-
- //启动下载
- [self beginDownload];
- }
- /** 删除任务(删除下载url内容的任务) */
- - (void)deleteDownloadWithUrl:(NSString *)url
- {
- HLog(@"deleteDownloadWithUrl")
- NSMutableArray *curDownloadingOperationArr = [NSMutableArray arrayWithArray:self.downloadingOperationArr];
-
- NSMutableArray *curWaitingUrlArr = [NSMutableArray arrayWithArray:self.downloadWaitingUrlArr];
-
-
- //1.检测等待下载的任务
- BOOL isWaitingUrlType = NO;
- for (NSString*waitingUrl in curWaitingUrlArr) {
- if ([waitingUrl isEqualToString:url]) {
- [self.downloadWaitingUrlArr removeObject:waitingUrl];
- isWaitingUrlType = YES;
- break;
- }
- }
-
- //2.检测下载中的任务
- if(!isWaitingUrlType){
- for (customDownloadOperation *operationDoing in curDownloadingOperationArr) {
- if([operationDoing.url isEqualToString:url]){
- operationDoing.isManualCancel = YES;
- if(operationDoing.dataTask){
- [operationDoing.dataTask cancel];
- }
- // operationDoing.dataTask = nil;
- // operationDoing.session = nil;
- //
- // [operationDoing.handle closeFile];
- // operationDoing.handle = nil;
- //
- // [self.downloadingOperationArr removeObject:operationDoing];
- break;
- }
- }
- }
-
- //3. 删除本地文件
- [customDownloadCacheManager deleteFileWithUrl:url];
-
- //4.进行下一个任务
- //[self beginDownload];
- }
- /** 暂停任务(暂停下载url内容的任务) */
- - (void)supendDownloadWithUrl:(NSString *)url
- {
- HLog(@"supendDownloadWithUrl")
- NSMutableArray *curDownloadingOperationArr = [NSMutableArray arrayWithArray:self.downloadingOperationArr];
-
- NSMutableArray *curWaitingUrlArr = [NSMutableArray arrayWithArray:self.downloadWaitingUrlArr];
-
-
- //1.检测等待下载的任务
- for (NSString*waitingUrl in curWaitingUrlArr) {
- if ([waitingUrl isEqualToString:url]) {
- [self.downloadWaitingUrlArr removeObject:waitingUrl];
- break;
- }
- }
-
- //2.检测下载中的任务
- for (customDownloadOperation *operationDoing in curDownloadingOperationArr) {
- if([operationDoing.url isEqualToString:url]){
- operationDoing.isManualCancel = YES;
-
- if(operationDoing.dataTask){
- [operationDoing.dataTask cancel];
- }
- // operationDoing.dataTask = nil;
- // operationDoing.session = nil;
- //
- // [operationDoing.handle closeFile];
- // operationDoing.handle = nil;
- //
- // //删除后 didCompleteWithError 不再处理
- // [self.downloadingOperationArr removeObject:operationDoing];
-
- break;
- }
- }
-
- //进行下一个任务
- //[self beginDownload];
- }
- /** 暂停当前所有的下载任务 下载任务不会从列队中删除 */
- - (void)suspendAllDownloadTask
- {
- HLog(@"suspendAllDownloadTask")
- NSMutableArray *curDownloadingOperationArr = [NSMutableArray arrayWithArray:self.downloadingOperationArr];
-
- //1.删除等待下载的任务
- [self.downloadWaitingUrlArr removeAllObjects];
-
- //2.检测下载中的任务
- for (customDownloadOperation *operationDoing in curDownloadingOperationArr) {
- operationDoing.isManualCancel = YES;
- if(operationDoing.dataTask){
- [operationDoing.dataTask cancel];
- }
-
- // operationDoing.dataTask = nil;
- // operationDoing.session = nil;
- // [operationDoing.handle closeFile];
- // operationDoing.handle = nil;
- }
- [self.downloadingOperationArr removeAllObjects];
- }
- #pragma mark 重新启动因为网络失败而停止的任务
- - (void)reDownloadNetworkTaskBy:(NSString*)url
- {
- @synchronized (self) {
- [self.downloadWaitingUrlArr insertObject:url atIndex:0];
- }
-
- [self beginDownload];
- }
- #pragma mark - <NSURLSessionDataDelegate>
- // ssl 服务 证书信任
- - (void)URLSession:(NSURLSession *)session
- didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
- completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{
- if(![challenge.protectionSpace.authenticationMethod isEqualToString:@"NSURLAuthenticationMethodServerTrust"]) {
- return;
- }
-
- // 信任该插件
- NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust];
- // 第一个参数 告诉系统如何处置
- completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
- }
- //当请求协议是https的时候回调用该方法
- //Challenge 挑战 质询(受保护空间)
- //NSURLAuthenticationMethodServerTrust 服务器信任证书
- - (void)URLSession:(NSURLSession *)session
- task:(NSURLSessionTask *)task
- didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
- completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler {
-
- if(![challenge.protectionSpace.authenticationMethod isEqualToString:@"NSURLAuthenticationMethodServerTrust"]) {
- return;
- }
-
- // 信任该插件
- NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust];
- // 第一个参数 告诉系统如何处置
- completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
-
- }
- // 接受到响应调用
- - (void)URLSession:(NSURLSession *)session
- dataTask:(NSURLSessionDataTask *)dataTask
- didReceiveResponse:(NSURLResponse *)response
- completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
-
- // 将响应交给列队处理
- BOOL canDownload = [self handleOperateby:dataTask WithResponse:response];
-
- if(canDownload){
- // 允许下载
- completionHandler(NSURLSessionResponseAllow);
- }
- else{
- // 不允许下载
- completionHandler(NSURLSessionResponseCancel);
- }
-
- }
- // 接受到数据碎片 的时候调用,调用多次
- - (void)URLSession:(NSURLSession *)session
- dataTask:(NSURLSessionDataTask *)dataTask
- didReceiveData:(NSData *)data {
- // 接收到session 下载碎片交个列队管理
- [self dataTask:dataTask didReceiveData:data];
- }
- // <NSURLSessionDataDelegate> 完成下载
- - (void)URLSession:(NSURLSession *)session
- task:(NSURLSessionTask *)task
- didCompleteWithError:(nullable NSError *)error {
- [self task:task didCompleteWithError:error];
- }
- - (void)URLSession:(NSURLSession *)session
- task:(NSURLSessionTask *)task
- needNewBodyStream:(void (^)(NSInputStream * _Nullable bodyStream))completionHandler {
-
- }
- - (void)URLSession:(NSURLSession *)session
- task:(NSURLSessionTask *)task
- didSendBodyData:(int64_t)bytesSent
- totalBytesSent:(int64_t)totalBytesSent
- totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
-
- }
- #pragma mark 处理接收到的数据
- // 接收到相应时
- - (BOOL)handleOperateby:(NSURLSessionTask*)dataTask WithResponse:(NSURLResponse *)response {
-
- HLog(@"handleOperateby")
- customDownloadOperation *operation = nil;
- NSMutableArray *curDownloadingOperationArr = [NSMutableArray arrayWithArray:self.downloadingOperationArr];
- HLog(@"handleOperateby 开始遍历");
- for (customDownloadOperation *operationDoing in curDownloadingOperationArr) {
- if(operationDoing.dataTask == dataTask){
- operation = operationDoing;
- break;
- }
- }
-
- if (!operation) {
- HLog(@"没找到当前下载任务");
- operation.downloadState = customDownloadStateFailed;
- [dataTask cancel];
- return NO;
- }
-
- // 检查response是否是NSHTTPURLResponse的实例
- if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
- NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
- NSInteger statusCode = httpResponse.statusCode;
- HLog(@"HTTP Status Code: %ld", (long)statusCode);
-
- if(statusCode == 404){
- operation.downloadState = customDownloadStateFailed;
- operation.isFile404Cancel = YES;
- return NO;
- }
-
- }
-
- // 总的size
- if (operation.currentSize + response.expectedContentLength == 0) {
- HLog(@"下载数据回调异常");
- operation.downloadState = customDownloadStateFailed;
- return NO;
- }
- operation.totalSize = operation.currentSize + response.expectedContentLength;
-
- //判断手机磁盘空间是否有剩余
- if(![iTools checkFreeDiskSpaceInBytesIsOK:operation.totalSize + 500*1024*1024]){
-
- operation.downloadState = customDownloadStateFailed;
- [dataTask cancel];
-
- mainBlock(^{
- [[iToast makeText:NSLocalizedString(@"phone_space_Insufficient",nil)] show];
- });
- return NO;
- }
-
- // 创建空的文件夹
- if (operation.currentSize == 0) {
- // 创建空的文件
- [[NSFileManager defaultManager] createFileAtPath:operation.fullPath contents:nil attributes:nil];
- }
-
- // 创建文件句柄
- operation.handle = [NSFileHandle fileHandleForWritingAtPath:operation.fullPath];
-
- // 文件句柄移动到文件末尾 位置 // 返回值是 unsign long long
- [operation.handle seekToEndOfFile];
-
- // 开始下载记录文件下载信息
- operation.downloadState = customDownloadStateDoing;
-
- return YES;
- }
- - (void)dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
- {
- HLog(@"didReceiveData")
- customDownloadOperation *operation = [self checkCurrentOperationByDataTask:dataTask withNeedDelete:NO];
- // NSMutableArray *curDownloadingOperationArr = [NSMutableArray arrayWithArray:self.downloadingOperationArr];
- // HLog(@"didReceiveData 开始遍历");
- // for (customDownloadOperation *operationDoing in curDownloadingOperationArr) {
- // if(operationDoing.dataTask == dataTask){
- // operation = operationDoing;
- // break;
- // }
- // }
-
- if (!operation) {
- HLog(@"没找到当前下载任务");
- [dataTask cancel];
- return;
- }
-
- //手动取消任务
- if (operation.isManualCancel) {
- return;
- }
-
- // 获得已经下载的文件大小
- operation.currentSize += data.length;
- HLog(@"当前线程:%@ -----currentSize:%lld---progress:%.2f",[NSThread currentThread], operation.currentSize, 1.00*operation.currentSize/operation.totalSize);
- // 写入文件
- if(operation.handle){
- [operation.handle writeData:data];
- }
- // 下载中通知
- [self operationDoningWithOperation:operation];
- }
- - (void)task:(NSURLSessionTask *)dataTask didCompleteWithError:(NSError *)error
- {
- HLog(@"didCompleteWithError")
- customDownloadOperation *operation = [self checkCurrentOperationByDataTask:dataTask withNeedDelete:YES];
- // NSMutableArray *curDownloadingOperationArr = [NSMutableArray arrayWithArray:self.downloadingOperationArr];
- // HLog(@"didCompleteWithError 开始遍历");
- // for (customDownloadOperation *operationDoing in curDownloadingOperationArr) {
- // if(operationDoing.dataTask == dataTask){
- // operation = operationDoing;
- // [self.downloadingOperationArr removeObject:operationDoing];
- // break;
- // }
- // }
-
- if (!operation) {
- HLog(@"没找到当前下载任务");
- //[dataTask suspend];
- return;
- }
-
- if(operation.dataTask){
- //[operation.dataTask cancel];
- operation.dataTask = nil;
- operation.session = nil;
-
- if(operation.handle){
- // 关闭文件句柄
- [operation.handle closeFile];
- // 释放文件句柄
- operation.handle = nil;
- }
- }
-
- // -1005 网络中断 1009 网络似乎中断
- if(error && (error.code == -1005 || error.code == -1009) && !operation.isFile404Cancel){//网络中断
- HLog(@"reDownloadNetworkTaskBy");
- //延时几秒再次启动这个任务
- // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- // [self reDownloadNetworkTaskBy:operation.url];
- // });
-
- [[nasDownloadFileManager shareInstance] saveDownloadloadingTaskByNetWorkErrorFun];
-
- return;
- }
-
- if(operation.isManualCancel){
- [self beginDownload];
- return;
- }
-
- // 完成下载 通知 block
- if (error ||operation.downloadState == customDownloadStateFailed) {
- [self operationFailedWithOperation:operation];
- } else {
- operation.isFinished = YES;
- operation.timeStamp = [iTools getNowTimeStamp];
- [self operationSuccessWithOperation:operation];
- }
-
- [self beginDownload];
- }
- #pragma mark 查询正在操作的 operation 并且看是否要删除
- - (customDownloadOperation *)checkCurrentOperationByDataTask:(NSURLSessionTask *)dataTask withNeedDelete:(BOOL)needDelete
- {
- customDownloadOperation *operation = nil;
- [_lock lock];
-
- NSMutableArray *curDownloadingOperationArr = [NSMutableArray arrayWithArray:self.downloadingOperationArr];
- HLog(@"开始遍历 查询当前下载任务");
- for (customDownloadOperation *operationDoing in curDownloadingOperationArr) {
- if(operationDoing.dataTask == dataTask){
- operation = operationDoing;
- if(needDelete){
- [self.downloadingOperationArr removeObject:operationDoing];
- }
- break;
- }
- }
- HLog(@"结束遍历 查询当前下载任务");
-
- [_lock unlock];
-
- return operation;
- }
- #pragma mark 发送通知
- // 失败某一个operation 保存本地 通知外界
- - (void)operationFailedWithOperation:(customDownloadOperation *)operation {
- HLog(@"DownloadTaskExeError \n %@",operation.url);
- operation.downloadState = customDownloadStateFailed;
- [[NSNotificationCenter defaultCenter] postNotificationName:nasDownloadTaskExeError object:nil userInfo:@{@"operation" : operation}];
- }
- // 重启某一个operation 保存本地 通知外界
- - (void)operationDoningWithOperation:(customDownloadOperation *)operation {
- HLog(@"DownloadTaskExeing");
-
- NSTimeInterval curTime = [[NSDate date] timeIntervalSince1970];
- NSTimeInterval timeDiff = curTime - operation.preNotTimeInterval;
-
- HLog(@"控制刷新时间为1秒:%f",timeDiff);
- CGFloat RefreshTimer = 0.8;
- if(operation.totalSize <= 5*1024*1024){//小于5M 事实
- RefreshTimer = 0.2;
- }
-
- if (operation.preNotTimeInterval <= 0
- || timeDiff > RefreshTimer ) {
-
- HLog(@"下载发送刷新通知:%f",timeDiff);
- operation.preNotTimeInterval = curTime;
- [[NSNotificationCenter defaultCenter] postNotificationName:nasDownloadTaskExeing object:nil userInfo:@{@"operation" : operation}];
- }
- }
- // 等待某一个operation 保存本地 通知外界
- - (void)operationSuccessWithOperation:(customDownloadOperation *)operation {
- HLog(@"DownloadTaskExeEnd \n %@",operation.url);
- operation.downloadState = customDownloadStateCompleted;
- [[NSNotificationCenter defaultCenter] postNotificationName:nasDownloadTaskExeEnd object:nil userInfo:@{@"operation" : operation}];
- }
- -(NSURLSession*)creatNewSessionFun
- {
- NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
- // 设置请求超时
- config.timeoutIntervalForRequest = -1;
- // config.networkServiceType = NSURLNetworkServiceTypeVideo;
- config.timeoutIntervalForResource = -1;
- // config.TLSMaximumSupportedProtocol = kSSLProtocolAll;
-
- //_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue currentQueue]];
- NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
-
- return session;
- }
- #pragma mark - lazy load
- - (NSMutableArray *)downloadWaitingUrlArr {
-
- if (!_downloadWaitingUrlArr) {
- _downloadWaitingUrlArr = [NSMutableArray array];
- }
- return _downloadWaitingUrlArr;
- }
- - (NSMutableArray *)downloadingOperationArr {
-
- if (!_downloadingOperationArr) {
- _downloadingOperationArr = [NSMutableArray array];
- }
- return _downloadingOperationArr;
- }
- - (BOOL)isDownLoadIngType
- {
- if(self.downloadWaitingUrlArr.count >= 1){
- return YES;
- }
-
- if(self.downloadingOperationArr.count >= 1){
- return YES;
- }
-
- return NO;
- }
- @end
|