iOS之功能细节收藏ios今日看点

【补充】NSURLSession 详解离线断点下载的实现

2016-10-20  本文已影响1925人  BWLi420

在上一篇文章开发只懂 AFN ?搞定 NSURLSession 才是硬道理中,我们已经对 NSURLSession 的基本使用有了简单认识,这里针对使用 NSURLSession 实现断点下载、离线断点下载等功能进行进一步拓展,希望看到这篇文章的朋友都能从中得到自己想要的知识。如有不足,欢迎指正!

NSURLSessionDataTask 大文件离线断点下载

1. 实现文件下载

//01 确定请求路径
NSURL *URL = [NSURL URLWithString:@"http://sony.it168.com/data/attachment/forum/201410/20/2154195j037033ujs7cio0.jpg"];
//02 创建会话对象 设置代理
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//03 创建请求 发送请求
[[session dataTaskWithURL:URL] resume];


###### 2. 监听文件的下载进度
- 利用代理来监听文件下载进度
- 计算文件的下载进度 = 已经下载的 / 文件的总大小

    ```objective-c
-(void)URLSession:(NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask 
didReceiveResponse:(nonnull NSURLResponse *)response 
completionHandler:(nonnull void (^)(NSURLSessionResponseDisposition))completionHandler {
        //子线程中执行
        NSLog(@"接收到服务器响应的时候调用 -- %@", [NSThread currentThread]);
    
        //得到请求文件的数据大小
        self.totalLength = response.expectedContentLength;
        //默认情况下不接收数据
        //必须告诉系统是否接收服务器返回的数据
        completionHandler(NSURLSessionResponseAllow);
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    
        NSLog(@"接受到服务器返回数据的时候调用,可能被调用多次");
        //拼接服务器返回的数据
        [self.fileData appendData:data];
        //计算文件的下载进度 = 已经下载的 / 文件的总大小
        self.progressView.progress = 1.0 * self.fileData.length / self.totalLength;
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    
        //保存数据 -> 沙盒
        NSString *fileName = task.response.suggestedFilename;
        NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        NSString *fullPath = [cachePath stringByAppendingPathComponent:fileName];
    
        [self.fileData writeToFile:fullPath atomically:YES];
        self.fileData = nil;
}

做完以上两步之后就可以实现文件的下载操作和监听下载进度,但是此时会有很多问题,比如:内存飙升下载进度错乱无法控制下载状态等等,对于这些存在的问题,我们下面将一一进行解决。

初步实现效果(存在问题)
3. 解决内存飙升问题
解决内存飙升
4. 常用操作:开始 | 暂停 | 取消 | 恢复
下载控制效果
5. 断点下载

self.totalLength = response.expectedContentLength + self.currentLength;

- 优化性能(以文件句柄方式为例,输出流同理):只有第一次接收到响应的时候才需要创建空的文件
  
    ```objective-c
if(self.currentLength == 0) {
        //在沙盒中创建一个空的文件
        [[NSFileManager defaultManager] createFileAtPath:fullPath contents:nil attributes:nil];
    }
断点下载效果
6. 离线断点下载

//获得之前已经下载的文件数据大小 => 获得沙盒中已经存在的文件数据大小
//获得某个路径对应文件的属性
NSDictionary *fileInfo = [[NSFileManager defaultManager] attributesOfItemAtPath:fullPath error:nil];
self.currentLength = [fileInfo fileSize];

- 此时离线断点下载的功能也已经做好,但是仍有一些小问题需要处理
- 优化:再次打开程序时,进度条为空,开始下载时会直接跳到当前进度值,造成用户体验不好
- 解决步骤:
    1. 在第一个代理方法中将文件的总大小写入到磁盘
    
        ```objective-c
    [[[NSString stringWithFormat:@"%zd",self.totalLength] dataUsingEncoding:NSUTF8StringEncoding] writeToFile:SizefullPath atomically:YES];
    ```
    2. 在 viewDidLoad 中做处理
    
        ```objective-c
    //显示文件的进度信息 = 已经下载文件数据大小(self.currentLength) / 文件的总大小
    NSData *totalSize = [NSData dataWithContentsOfFile:SizefullPath];
    self.totalLength = [[[NSString alloc]initWithData:totalSize encoding:NSUTF8StringEncoding] integerValue];
    if (self.totalLength != 0) {
            self.progressView.progress = 1.0 * self.currentLength/self.totalLength;
    }
    ```
![离线断点下载效果](http:https://img.haomeiwen.com/i2997426/f83e29a81892ce7f.gif?imageMogr2/auto-orient/strip)

#### 写在最后
> 以上就是使用 NSURLSession 实现离线断点下载的全部过程,由于个人水平有限,如有错误,敬请指正!如果觉得这篇文章对您有所帮助,请点击下方的喜欢或关注本人,谢谢您的支持!

- 实现源码地址:[https://github.com/mortal-master/BWOfflineDownload](https://github.com/mortal-master/BWOfflineDownload)
上一篇下一篇

猜你喜欢

热点阅读