iOS NSURLSession后台下载和断点续传

来源:互联网 发布:oracle sql书写技巧 编辑:程序博客网 时间:2024/05/22 18:00

前几天面试被人问到后台下载的问题,当时就GG了,之前写Andriod的时候,用service写过,但是iOS没有做过这方面的。回去后查了一些资料,就有了这篇博文。

NSURLSession出来之后,我们可以很容易实现后台下载这种任务,简单使用backgroundSessionConfigurationWithIdentifier:就OK了。
下面讲解如何实现:

首先让我们在ViewController的类拓展里添加如下代码:

@interface ViewController ()<NSURLSessionDelegate,NSURLSessionDownloadDelegate,NSURLSessionTaskDelegate>@property (nonatomic,strong) NSURLSession *session;@property (nonatomic,strong) NSURLSessionDownloadTask *task;@end

我们在里面声明里NSURLSession和NSURLSessionDownTask的属性,还实现了NSURLSessionDelegate,NSURLSessionDownloadDelegate,NSURLSessionTaskDelegate这三个必须实现的代理。

接着我们去实现下载的方法:

- (void)downloadBackground{    NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"download"];    //系统根据当前性能自动处理后台任务的优先级    config.discretionary = YES;    _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];    //下面两个是不同的文件,一个是一张图片,一个是微博,大家可以选择不同的网址去实验    // http://farm3.staticflickr.com/2846/9823925914_78cd653ac9_b_d.jpg    // http://dlsw.baidu.com/sw-search-sp/soft/3f/12289/Weibo.4.5.3.37575common_wbupdate.1423811415.exe    NSURL *url = [NSURL URLWithString:@"http://dlsw.baidu.com/sw-search-sp/soft/3f/12289/Weibo.4.5.3.37575common_wbupdate.1423811415.exe"];    NSURLRequest *request = [NSURLRequest requestWithURL:url];    _task = [_session downloadTaskWithRequest:request];    [_task resume];}

NSURLSession的固定写法,定义后台下载的NSURLSessionConfiguration,定义session,再定义一个request和task,启动task。

这样就可以了吗?当然不是,我们还需要实现相应的代理方法,再添加下面的方法

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{    //下载过程不断回调    NSLog(@"当前进度%f%%",totalBytesWritten * 1.0 / totalBytesExpectedToWrite * 100);}- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{    //下载结束回调,注意这里得到的文件是一个临时文件,为了保存这个文件,我们需要把它copy到Document目录下    NSLog(@"file--%@,path--%@",downloadTask.description,location);}

好了,在viewDidLoad函数中调用downloadBackground,现在运行工程,应该可以看到不断打印的进度,在模拟器按一下command+shift+h回到桌面,发现程序依然在下载,并没有停滞,不过最后发现打印出来的path是.tmp文件,像上面注释说的,这里我们得到的是一个临时文件,我们必须将这个文件copy到Document目录下。当然如果你什么都看不到,只看到一条报错信息,请确保你已经在你的plist文件加入以下键值对:

 <key>NSAppTransportSecurity</key>    <dict>        <key>NSAllowsArbitraryLoads</key>        <true/>    </dict>

为什么要这样写?请度娘!!!!

好了,现在让我们实现copy文件的函数

- (void)copyFileAtPath:(NSString *)path{    NSFileManager *fileManager = [NSFileManager defaultManager];    NSError *error;    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);    NSString *documentDirectory = [paths objectAtIndex:0];    NSLog(@"document--%@",documentDirectory);  //测试图片的网址,改这个  //    NSString *toPath = [documentDirectory stringByAppendingPathComponent:@"1.jpg"];    NSString *toPath = [documentDirectory stringByAppendingPathComponent:@"weibo.exe"];    if (![fileManager fileExistsAtPath:toPath]) {        [fileManager copyItemAtPath:path toPath:toPath error:&error];        if (error) {            NSLog(@"copy error--%@",error.description);        }     }}

上面这个函数首先得到Document文件夹的路径,再拼接出文件路径,最后就是判断文件是否存在,不存在就复制。

请在下载完成的回调函数里调用这个方法,等到下载完成之后,复制控制台打印的document路径,在桌面按command+shift+g,会弹出前往的窗口,粘贴刚刚复制的路径,点击前往,你会看到这样的图片:

这里写图片描述

做到这里,后台下载就完成了。不过我们可以做得更好一些,让我们给它加上断点续传的功能。先让我们用StoryBoard拉个简单的界面,不熟悉StroyBoard的请自行度娘。界面大概是这样的:

这里写图片描述

接着在类拓展里添加下面的属性:

@property (nonatomic,strong) NSData *data;@property (weak, nonatomic) IBOutlet UIProgressView *progressView;

记住progressView属性是拉出来的,是拉出来的,是拉出来的。接着再拉出下面三个方法,分别添加如下代码:

- (IBAction)startAction:(UIButton *)sender {     [self downloadBackground];}- (IBAction)pauseAction:(UIButton *)sender {    if (!_task) {        return;    }    [_task cancelByProducingResumeData:^(NSData * _Nullable resumeData) {        _data = resumeData;    }];}- (IBAction)resumeAction:(UIButton *)sender {    if (!_data) {        return;    }else {        _task = [_session downloadTaskWithResumeData:_data];        [_task resume];    }}

解释一下上面的代码,首先把downloadBackground方法移到开始按钮的点击事件里,pauseAction主要就是调用cancelByProducingResumeData方法,用data属性记录resumeData,在resumeAction里重新启动task,注意是调用downloadTaskWithResumeData方法。

做到这里还没有完哦,我们的progressView还没有刷新呢。所以在下载过程调用的回调代理里添加代码:

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{    NSLog(@"当前进度%f%%",totalBytesWritten * 1.0 / totalBytesExpectedToWrite * 100);    //因为这里不是主线程,所以需要回到主线程刷新UI    dispatch_async(dispatch_get_main_queue(), ^{        self.progressView.progress = totalBytesWritten * 1.0 / totalBytesExpectedToWrite;    });}

现在再运行,你会看到下面的效果:

这里写图片描述

好了,这就完成了后台下载和断点续传了。代码请点链接,文件夹为后台下载和本地通知

0 1