iOS关于使用NSURLSession进行大文件下载以及断点下载

Posted ThomasYB

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS关于使用NSURLSession进行大文件下载以及断点下载相关的知识,希望对你有一定的参考价值。

NSURLSession 可以发送Get/Post请求,实现文件的下载和上传。
在NSURLSesiion中,任何请求都可以被看做是一个任务。其中有三种任务类型

// NSURLSessionDataTask : 普通的GET\\POST请求
// NSURLSessionDownloadTask : 文件下载
// NSURLSessionUploadTask : 文件上传(很少用,一般服务器不支持)

NSURLSession 简单使用

NSURLSession发送请求非常简单,与connection不同的是,任务创建后不会自动发送请求,需要手动开始执行任务。

 // 1.得到session对象

    NSURLSession*session = [NSURLSession sharedSession];

    NSURL* url = [NSURLURLWithString:@""];

 

    // 2.创建一个task,任务

    NSURLSessionDataTask*dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse*response, NSError *error)

        // data 为返回数据

    ];

 

    // 3.开始任务

    [dataTaskresume];

// 发送post请求 自定义请求头

[session dataTaskWithRequest:<#(NSURLRequest *)#>completionHandler:<#^(NSData *data, NSURLResponse *response, NSError*error)completionHandler#>]

NSURLSession 下载

使用NSURLSession就非常简单了,不需要去考虑什么边下载边写入沙盒的问题,苹果都帮我们做好了。代码如下

 NSURL* url = [NSURLURLWithString:@"http://dlsw.baidu.com/sw-search-sp/soft/9d/25765/sogou_mac_32c_V3.2.0.1437101586.dmg"];

 

    // 得到session对象

    NSURLSession*session = [NSURLSession sharedSession];

 

    // 创建任务

    NSURLSessionDownloadTask*downloadTask = [session downloadTaskWithURL:url completionHandler:^(NSURL*location, NSURLResponse *response, NSError *error)

 

    ];

    // 开始任务

    [downloadTaskresume];

是不是跟NSURLConnection很像,但仔细看会发现回调的方法里面并没用NSData传回来,多了一个location,顾名思义,location就是下载好的文件写入沙盒的地址,打印一下发现下载好的文件被自动写入的temp文件夹下面了。

location:file:///Users/yeaodong/Library/Developer/CoreSimulator/Devices/E52B4B95-53E1-46A2-9881-8C969958FBC0/data/Containers/Data/Application/BFB9F0CA-0F50-4682-BBBD-B71B54C39EBE/tmp/CFNetworkDownload_YNnuIS.tmp

这里写图片描述

不过在下载完成之后会自动删除temp中的文件,所有我们需要做的只是在回调中把文件移动(或者复制,反正之后会自动删除)到caches中。

NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask, YES) lastObject];

        //response.suggestedFilename : 建议使用的文件名,一般跟服务器端的文件名一致

        NSString*file = [caches stringByAppendingPathComponent:response.suggestedFilename];

 

        // 将临时文件剪切或者复制Caches文件夹

        NSFileManager*mgr = [NSFileManager defaultManager];

 

        // AtPath :剪切前的文件路径

        // ToPath :剪切后的文件路径

        [mgrmoveItemAtPath:location.path toPath:file error:nil];

不过通过这种方式下载有个缺点就是无法监听下载进度,要监听下载进度,苹果通常的作法是通过delegate,这里也一样。而且NSURLSession的创建方式也有所不同。
首先遵守协议<NSURLSessionDownloadDelegate> 注意不要写错
点进去发现协议里面有三个方法。

#pragma mark -- NSURLSessionDownloadDelegate

/**

 *  下载完毕会调用

 *

 *  @param location     文件临时地址

 */

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask*)downloadTask

didFinishDownloadingToURL:(NSURL *)location

/**

 *  每次写入沙盒完毕调用

 *  在这里面监听下载进度,totalBytesWritten/totalBytesExpectedToWrite

 *

 *  @param bytesWritten              这次写入的大小

 *  @param totalBytesWritten         已经写入沙盒的大小

 *  @param totalBytesExpectedToWrite 文件总大小

 */

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask*)downloadTask

     didWriteData:(int64_t)bytesWritten

 totalBytesWritten:(int64_t)totalBytesWritten

totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite

    self.pgLabel.text= [NSString stringWithFormat:@"下载进度:%f",(double)totalBytesWritten/totalBytesExpectedToWrite];

 

/**

 *  恢复下载后调用,

 */

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask*)downloadTask

 didResumeAtOffset:(int64_t)fileOffset

expectedTotalBytes:(int64_t)expectedTotalBytes

 

这上面的注释已经很详细了,相信大家都能看懂吧。

NSURLSession创建方式,这里就不能使用Block回调方式了,如果给下载任务设置了completionHandler这个block,也实现了下载的代理方法,优先执行block,代理方法也就不会执行了。

// 得到session对象

    NSURLSessionConfiguration*cfg = [NSURLSessionConfiguration defaultSessionConfiguration]; // 默认配置

 

    NSURLSession*session = [NSURLSession sessionWithConfiguration:cfg delegate:selfdelegateQueue:[NSOperationQueue mainQueue]];

 

    // 创建任务

    NSURLSessionDownloadTask*downloadTask = [session downloadTaskWithURL:url];

 

    // 开始任务

    [downloadTaskresume];

相比之前的NSURLConnection方式简单很多吧,用NSURLSessionDownloadTask做断点下载也很简单,我们先了解一下任务的取消方法

- (void)cancelByProducingResumeData:(void (^)(NSData*resumeData))completionHandler;

取消操作以后会调用一个Block,并传入一个resumeData,该参数包含了继续下载文件的位置信息。也就是说,当你下载了10M得文件数据,暂停了。那么你下次继续下载的时候是从第10M这个位置开始的,而不是从文件最开始的位置开始下载。因而为了保存这些信息,所以才定义了这个NSData类型的这个属性:resumeData。这个data只包含了url跟已经下载了多少数据,不会很大,不用担心内存问题。

另外,session还提供了通过resumeData来创建任务的方法

- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData*)resumeData;

我们只需要在取消操作的回调中记录好resumeData,然后在恢复下载的适合通过上面的方法创建任务就好了,相比NSURLconnection简单太多了。
需要注意的是Block中循环引用的问题

__weak typeof(self) selfVc = self;

    [self.downloadTaskcancelByProducingResumeData:^(NSData *resumeData)

        selfVc.resumeData= resumeData;

        selfVc.downloadTask= nil;

    ];

示例程序下载:https://github.com/hongfenglt/HFDownLoad


以上是关于iOS关于使用NSURLSession进行大文件下载以及断点下载的主要内容,如果未能解决你的问题,请参考以下文章

iOS网络开发使用NSURLSession

iOS开发之网络编程--2NSURLSessionDownloadTask文件下载

#iOS问题记录#关于NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9801)

在 iOS8 中使用 NSUrlSession 使用流请求进行后台上传

NSURLSession 后台传输不起作用

iOS:如何增加 NSURLSession 上传任务的块大小?