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

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

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

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<NSCoding>

@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 博客写的很详细 如何配置

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

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSURLSessionDownloadURL</key> //资源的 URL
    <string>http://dlhjhx.xicp.net:13081/firmware/E302620170303.pkg</string>
    <key>NSURLSessionResumeBytesReceived</key>//录下来了已经下载完成的字节数
    <integer>68441</integer>
    <key>NSURLSessionResumeCurrentRequest</key>//当前请求时的 NSURLRequest 对象
    <data>
    YnBsaXN0MDDUAQIDBAUGdXZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3AS
     //省略一万字
    BcMF0AXTBeAF8gX1BhMAAAAAAAACAQAAAAAAAAB5AAAAAAAAAAAAAAAAAAAGFQ==
    </data>
    <key>NSURLSessionResumeEntityTag</key>
    <string>W/"492894-1488511050000"</string>
    <key>NSURLSessionResumeInfoTempFileName</key>//下载过程中临时文件所存储的位置,存储在应用程序 tmp 文件夹下
    <string>CFNetworkDownload_kv1UKk.tmp</string>
    <key>NSURLSessionResumeInfoVersion</key>
    <integer>2</integer>
    <key>NSURLSessionResumeOriginalRequest</key>//初始请求时的 NSURLRequest 对象
    <data>
    YnBsaXN0MDDUAQIDBAUGUFFYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3AS
    //省略一万字
    BAwEEQQeBCEELgRABEMEYQAAAAAAAAIBAAAAAAAAAFQAAAAAAAAAAAAAAAAAAARj
    </data>
    <key>NSURLSessionResumeServerDownloadDate</key>
    <string>Fri, 03 Mar 2017 03:17:30 GMT</string>
</dict>
</plist>

每次启动的时候如果遇到未下载完成的任务就会替换任务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年的和呀,告诫大家要多强身健体呀,我这体会到身体是革命的本钱,没有好身体啥都搞不了,如配图呀,三月不努力,四月徒伤悲,五月就没脸见人啦。要个当一个能撑得起世界的男人

双臂可以撑得起世界的男人
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容