NSURLSession介绍以及断点下载(完整)

注意点:

#1,文件的保存路径不能是网址!!!
#2,NSOutputStream的作用就是保存网路下载的数据。

1,NSHTTPURLResponse响应对象

1,statusCode:状态码,可以根据这个值判断是否请求出错。
2,allHeaderFields:获得响应体内容
3,URL:一般使用在重定向,如果不需要重定向,响应的url和请求的url是一样的。
4,MIMEType:服务器告诉客户端返回的数据类型
5,textEncodingName :服务器告诉客户端返回内容的编码格式
6,expectedContentLength:服务器返回数据的长度,客户端可以通过该属性获得文件大小
7,suggestedFilename:服务器建议客户端保存文件使用的名字

2,参考:iOS中流(NSStream)的使用

#NSInputStream 和 NSOutputStream
1,NSInputStream 和 NSOutputStream 是NSStream的两个子类,分别对应了读文件和 写文件。
2,NSInputStream 和 NSOutputStream其实是对 CoreFoundation 层对应的CFReadStreamRef 和 CFWriteStreamRef 的高层抽象。

3,断点续传

#1,NSURLSessionTask是一个抽象类,本身不能使用,只能使用它的子类
NSURLSessionDataTask((完美断点)下载)、
NSURLSessionUploadTask(上传)、
NSURLSessionDownloadTask((有缺陷断点)下载)

注意:请求的时候,只要有completionHandler结果回调的,那么都不会走代理方法。只有下载完成之后才会走completionHandler回调。

#2,NSURLSessionDownloadTask实现断点下载(有缺陷)
//如果任务,取消了那么以后就不能恢复了
     //    [self.downloadTask cancel];
     
     //如果采取这种方式来取消任务,那么该方法会通过resumeData保存当前文件的下载信息
     //只要有了这份信息,以后就可以通过这些信息来恢复下载
     [self.downloadTask cancelByProducingResumeData:^(NSData * __nullable resumeData) {
     self.resumeData = resumeData;
     }];
     
     -----------
     //继续下载
     //首先通过之前保存的resumeData信息,创建一个下载任务
     self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];
     
     [self.downloadTask resume];
     
     局限性:
     
     01 如果用户点击暂停之后退出程序,那么需要把恢复下载的数据写一份到沙盒,代码复杂度增加
     02 如果用户在下载中途未保存恢复下载数据即退出程序,则不具备可操作性
#3,NSURLSessionDownloadTask两种请求方法介绍
//    1,该方法不会默认保存存到沙盒tmp文件中(可以通过NSURLSessionDownloadTask并以代理的方式来完成大文件的下载,但是需要手动存到沙盒中)
        NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithURL:url];

//    2,内部默认已经实现了边下载边写入沙盒tmp文件中操作,所以不用开发人员担心内存问题
    //注意,tmp中存储的后缀是.tmp,可以通过剪切文件来修改后缀。
    //    缺点:不能监听下载的进度。
//    NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {}];

4,任务的三种操作

    #启动task:
    [dataTask resume];
    #取消task
    [dataTask cancel];
    #暂停task
    [dataTask suspend];

5,实现断点下载最好的方式是使用NSURLSessionDataTask实现大文件离线断点下载

#流程:
文件是否下载完成-->文件下载中还是暂停下载-->是否存在文件下载的目录-->创建流,用NSURLSessionDataTask实现断点下载(如下)
#注意:一个下载文件URL需要两部分:task和stream
#HSFileName(url):表示通过URL加密后对应的文件名
#通过taskIdentifier获取task的时候,需要强转一下
//根据url获得对应的下载任务
- (NSURLSessionDataTask *)getTask:(NSString *)url
{
    return (NSURLSessionDataTask *)[self.tasks valueForKey:HSFileName(url)];
}
#正文:
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
    
    // 创建流
    NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:HSFileFullpath(url) append:YES];
    
    // 创建请求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
    
    // 设置请求头(从上次下载的长度开始继续下载)
    NSString *range = [NSString stringWithFormat:@"bytes=%zd-", HSDownloadLength(url)];
    [request setValue:range forHTTPHeaderField:@"Range"];
    
    // 创建一个Data任务(若之前已经下载过一部分的文件了,那么这里是从上次下载到的位置开始下载的,这里创建的task就会赋值上新的taskIdentifier,并且把原来URL对应的task替换掉。)
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
    NSUInteger taskIdentifier = arc4random() % ((arc4random() % 10000 + arc4random() % 10000));
    [task setValue:@(taskIdentifier) forKeyPath:@"taskIdentifier"];

    // 保存任务
    [self.tasks setValue:task forKey:HSFileName(url)];

    HSSessionModel *sessionModel = [[HSSessionModel alloc] init];
    sessionModel.url = url;
    sessionModel.progressBlock = progressBlock;
    sessionModel.stateBlock = stateBlock;
    sessionModel.stream = stream;
    [self.sessionModels setValue:sessionModel forKey:@(task.taskIdentifier).stringValue];
    //开始下载(然后就到代理方法中进行操作即可)
    [self start:url];

#pragma mark - 代理
#pragma mark NSURLSessionDataDelegate
/**
 * 接收到响应(进行打开下载任务对应的流,然后保存该文件的总大小到本地。)
 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    
    HSSessionModel *sessionModel = [self getSessionModel:dataTask.taskIdentifier];
    
    // 打开流
    [sessionModel.stream open];
    
    // 获得服务器这次请求 返回数据的总长度(Content-Length对应的是当前下载的URL对应的文件大小,也许文件已经下载过一部了,现在是继续下载的,所以这里后面又加上了已经下载的文件的大小)
    NSInteger totalLength = [response.allHeaderFields[@"Content-Length"] integerValue] + HSDownloadLength(sessionModel.url);
    sessionModel.totalLength = totalLength;
    
    //获取plist文件 (存储总长度到plist文件中,HSTotalLengthFullpath:plist文件的路径)
    NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:HSTotalLengthFullpath];
    if (dict == nil) dict = [NSMutableDictionary dictionary];
    dict[HSFileName(sessionModel.url)] = @(totalLength);
    [dict writeToFile:HSTotalLengthFullpath atomically:YES];
    
    // 接收这个请求,允许接收服务器的数据。(因为系统默认是不响应下载任务的)
    completionHandler(NSURLSessionResponseAllow);
}

/**
 * 接收到服务器返回的数据
 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
    HSSessionModel *sessionModel = [self getSessionModel:dataTask.taskIdentifier];
    
    // 写入数据
    [sessionModel.stream write:data.bytes maxLength:data.length];
    
    // 下载进度
    NSUInteger receivedSize = HSDownloadLength(sessionModel.url); //已经下载的文件的大小
    NSUInteger expectedSize = sessionModel.totalLength; //文件的总大小
    CGFloat progress = 1.0 * receivedSize / expectedSize;

    sessionModel.progressBlock(receivedSize, expectedSize, progress);
}

/**
 * 请求完毕(成功|失败)
 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    HSSessionModel *sessionModel = [self getSessionModel:task.taskIdentifier];
    if (!sessionModel) return;
    
    if ([self isCompletion:sessionModel.url]) {
        // 下载完成
        sessionModel.stateBlock(DownloadStateCompleted);
    } else if (error){
        // 下载失败
        sessionModel.stateBlock(DownloadStateFailed);
    }
    
    // 关闭流
    [sessionModel.stream close];
    sessionModel.stream = nil;
    
    // 清除任务
    [self.tasks removeObjectForKey:HSFileName(sessionModel.url)];
    [self.sessionModels removeObjectForKey:@(task.taskIdentifier).stringValue];
}

1,本文参考链接
2,NSURLSession上传文件

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,647评论 18 139
  • API定义规范 本规范设计基于如下使用场景: 请求频率不是非常高:如果产品的使用周期内请求频率非常高,建议使用双通...
    有涯逐无涯阅读 2,529评论 0 6
  • 一、概念(载录于:http://www.cnblogs.com/EricaMIN1987_IT/p/3837436...
    yuantao123434阅读 8,343评论 6 152
  • iOS开发系列--网络开发 概览 大部分应用程序都或多或少会牵扯到网络开发,例如说新浪微博、微信等,这些应用本身可...
    lichengjin阅读 3,654评论 2 7
  • 在学校从事英语教学十年有余,搭乘十个月二胎政策的春风车之后,不得不与我心爱的工作依依惜别。或许是好为人师,或者是旧...
    笨妈妈与淘气包阅读 275评论 0 1