基于NSURLSession 封装的一个多线程断点续传下载Demo

最近工作不是很忙,表示最近也在一直的学习理论知识,但是光是单纯的看书和看面试题,感觉有点大学期末考试的赶脚,所以还是想着要把理论和实践多结合,所以陆续把工作中的用到的东西整理成一个个Demo 这样,和尽量的封装一下子,这样也算是平时的一点积累。

首先是觉得可以把网络的小模块搞一搞,虽然AFN在ios界是神作,地位不可动摇,但是我们这还是不能单单只会AFN,必备的网络知识还是要了解,题外话图解HTTP这本书还是很不错的,生动形象,简单易懂的讲解了网络知识,强烈像我一样半路出家的人看一看,百度云链接。大神请绕行
废话说了一筐还是直接说代码吧,先看一下目录结构

基于NSURLSession 封装的一个多线程断点续传下载Demo_第1张图片
Demo目录结构.png

其实东西很简单,主要就是实现一个多线程下载的功能,在DownloadManagment 类中有一个字典,每次调用下载的时候都会建立一个NSURLSessionDownloadTask 下载任务,然后以URL为Key 把他存到字典中,这样这样可以防止重复执行下载任务,而且下载过程中可以暂停任务也就是支持断点续传,而且可以把执行多个任务,还有在任务之间进行切换。不过之前源代码实在不敢吐槽,居然把下载的一些缓存啥的都存到了数据库里,感觉好突兀,所以自己改成了存成归档文件,怎么存都可以随你喜欢啦,只不过不喜欢那么搞而已。大概把功能说道这里,下面就是最激动人心的贴代码时间。

首先是DownloadManagment类,负责对外管理类这么搞其实并说不清具体用到了什么设计模式,只不过各个牛逼的框架都会这么用。

@interface DownloadManagment  ()

@property(nonatomic, strong)NSMutableDictionary *downloadDic;//把不同的任务都存到了这个字典里方便管理

@end

@implementation DownloadManagment
+ (instancetype)shareDownloadManagment
{
    static DownloadManagment *downloadManagment = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        downloadManagment = [[DownloadManagment alloc]init];
    });
    return downloadManagment;
}

// 初始化方法
- (instancetype)init
{
    self = [super init];
    if (self) {
        self.downloadDic = [NSMutableDictionary dictionary];
    }
    return self;
}

// 根据URL添加一个下载类
- (Download *)addDownloadWithUrl:(NSString *)url
{
    // 先从字典里面取到对应的下载
    Download *download = self.downloadDic[url];
    if (download == nil) {// 如果字典里面没有 我们就创建一个
        download = [[Download alloc]initWithURL:url];
        //添加到我们的字典当中
        [self.downloadDic setObject:download forKey:url];
    }
    // 下载完成以后 让单例不再持有着下载类 从字典里面移除
    [download downloadCompleted:^(NSString *url) {
        [self.downloadDic removeObjectForKey:url];
        NSLog(@"下载完成");
    
    }];
    return download;
}

// 根据URL找到一个下载类
- (Download *)findDownloadWithURL:(NSString *)url
{
    return self.downloadDic[url];
}

// 返回所有正在下载的类
- (NSArray *)allDownlod
{
    return [self.downloadDic allValues];
}

其次是DownloadingManagment类,不知道原框架为什么会这么起,这其实就是一个Model累嘛,叫的人心都醉了。

@interface DownloadingManagment : NSObject

@property(nonatomic, copy)NSString *resumeDataStr;

@property(nonatomic, copy)NSString *fileSize;

@property(nonatomic, copy)NSString *filePath;

@property(nonatomic, copy)NSString * progress;

@property(nonatomic, copy)NSString *url;

@property(nonatomic, copy)NSString * time;

@property(nonatomic, copy)NSString * DownLoadType;
@end

重头戏是Download 类,所有的网络有关的东西都在这个类里实现
最开始是初始化类

- (id)initWithURL:(NSString *)url
{
    self = [super init];
    if (self) {
        
//        + (NSURLSessionConfiguration *)defaultSessionConfiguration;
//        //返回标准配置,这实际上与NSURLConnection的网络协议栈是一样的,具有相同的共享NSHTTPCookieStorage,共享NSURLCache和共享NSURLCredentialStorage。
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
        
        //设置请求超时为10秒钟
        config.timeoutIntervalForRequest = 10;
        
        //在蜂窝网络情况下是否继续请求(上传或下载)
        config.allowsCellularAccess = NO;
        
        
        self.session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
        
//        创建任务,把配置的信息,加到任务里,然后开始执行
        self.task = [_session downloadTaskWithURL:[NSURL URLWithString:url]];
        
        _url = url;
        
        // 先去数据库中查找是否已经下载了 如果已经下载了 我们就把_isFirst赋值为yes 防止重复添加数据
        _isFirst =[self findDownloadingWithURL:url];
        
        if (_isFirst) {
            // 如果已经下载了 我们更换我们的task
            self.task = [self.session downloadTaskWithResumeData:[self resumeDataWithURL:url]];
        } 
    }
    return self;
}

里面涉及到NSURLSessionConfiguration,具体的可以查看http://www.cnblogs.com/liugengqun/p/5140296.html 博客写的很详细 如何配置

接下来断点续传主要功能,把下载了的





    NSURLSessionDownloadURL //资源的 URL
    http://dlhjhx.xicp.net:13081/firmware/E302620170303.pkg
    NSURLSessionResumeBytesReceived//录下来了已经下载完成的字节数
    68441
    NSURLSessionResumeCurrentRequest//当前请求时的 NSURLRequest 对象
    
    YnBsaXN0MDDUAQIDBAUGdXZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3AS
     //省略一万字
    BcMF0AXTBeAF8gX1BhMAAAAAAAACAQAAAAAAAAB5AAAAAAAAAAAAAAAAAAAGFQ==
    
    NSURLSessionResumeEntityTag
    W/"492894-1488511050000"
    NSURLSessionResumeInfoTempFileName//下载过程中临时文件所存储的位置,存储在应用程序 tmp 文件夹下
    CFNetworkDownload_kv1UKk.tmp
    NSURLSessionResumeInfoVersion
    2
    NSURLSessionResumeOriginalRequest//初始请求时的 NSURLRequest 对象
    
    YnBsaXN0MDDUAQIDBAUGUFFYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3AS
    //省略一万字
    BAwEEQQeBCEELgRABEMEYQAAAAAAAAIBAAAAAAAAAFQAAAAAAAAAAAAAAAAAAARj
    
    NSURLSessionResumeServerDownloadDate
    Fri, 03 Mar 2017 03:17:30 GMT



每次启动的时候如果遇到未下载完成的任务就会替换任务Data中的已下载的字节,然后继续添加到任务重继续执行,这样就可以执行断点续传啦

/**
 恢复下载替换resumeDataStr 中生于的下载的字节

 @param url url description
 @return return value description
 */
- (NSData *)resumeDataWithURL:(NSString *)url
{
    // 1.找到下载中的model
    DownloadingManagment *downloading = [self findDownloadingWithURL:url];
    // 给progress赋初始值
    _progress = downloading.progress;
    NSLog(@"%@",downloading);
    // 2.获取当前下载的文件
    NSFileManager *fm = [NSFileManager defaultManager];
    
    NSString *fileSize = [NSString stringWithFormat:@"%llu", [fm attributesOfItemAtPath:downloading.filePath error:nil].fileSize];
    
    // 3.对resumeDataStr进行替换
    downloading.resumeDataStr = [downloading.resumeDataStr stringByReplacingOccurrencesOfString:downloading.fileSize withString:fileSize];
    // 4.生成NSData进行返回
    
    return [downloading.resumeDataStr dataUsingEncoding:NSUTF8StringEncoding];
}

最后没啥可说的了,下载完成后转移数据啥的,这玩意关键要根据实际的业务需求来啦,剩下的GitHub上有例子,有兴趣的童鞋可以下载看一下,我这水平有限,也就是抛砖引玉,大神请绕行。GitHub Demo

最后感慨最近体弱多病呀,药加起来吃的是我N年的和呀,告诫大家要多强身健体呀,我这体会到身体是革命的本钱,没有好身体啥都搞不了,如配图呀,三月不努力,四月徒伤悲,五月就没脸见人啦。要个当一个能撑得起世界的男人

基于NSURLSession 封装的一个多线程断点续传下载Demo_第2张图片
双臂可以撑得起世界的男人

你可能感兴趣的:(基于NSURLSession 封装的一个多线程断点续传下载Demo)