在iOS 7之前使用的网络服务是通过使用NSURLConnection中的全局状态来管理Cookie和身份验证,因此可能导致两个不同的链接同时使用这个设置时产生冲突。在iOS7的NSURLSession 中苹果公司决定改掉这个被人诟病的毛病。
在这里我们将使用NSURLSession来王成三种不同的下载方案。下面的文章我们将实现:
Simple dowload
如果我们要利用NSURLSession进行数据传输我们需要做如下的步骤
1.创建一个NSURLSessionConfiguration
2.创建NSSession并通过第一步创建好的NSURLSessionConfiguration来设置工作模式和网络设置
工作模式分为:
默认模式(default):工作模式类似鱼原来的NSURLConnection,可以使用缓存的Cache,cookie 等
实时模式(ephemeral):不是用缓存的Cache,Cookie和授权
后台模式 (background):在后台完成上传和下载,创建Configuration对象的时候需要给NSString一个ID用于追踪完成工作的Session是那一个。
对于简单的下载我们将只使用默认的会话:
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
一旦一个配置对象被创建那么它的属性就可以控制它的动作。例如,它可以设置接收TLS安全级别,不管cookies是否允许和是否超时。这里两个比较有趣的特性是allowsCellularAccess和discretionary。前者指定的设备是否被允许运行的网络session,只有一个蜂窝数据可用。设置一个会话作为全权使操作系统来安排网络访问明智的时间 - 也就是当一个无线网络可用,而当该设备具有良好的动力性。这主要是利用background session,因此默认为true的background session。
一旦我们有一个会话配置对象,我们可以创建会话本身。
NSURLSession *inProcessSession;
inProcessSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
注意这里我们也设置自己作为自己的一个委托。委托方法用来通知我们的数据传输的进度和当挑战验证请求信息 我们将很快实施一些适当的方法。
数据传输封装在任务 - 其中有三种类型:
NSURLSessionUploadTask:上传用的Task,传完之后不会再下载返回结果;
NSURLSessionDownloadTask:下载用的Task;
NSURLSessionDataTask:可以上传内容,上传完成之后再进行下载。
为了在会话中进行传输,我们需要创建一个任务。对于一个简单的文件下载:
NSString *url = @"hettp://appropriate/url/here";
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
NSURLSessionDownloadTask *cancellabelTask = [inProcessSession downloadTaskWithRequest:request];
[cancellableTask resume];
这一切就这么简单 - 现在会话将异步尝试下载该文件在指定的URL。
为了弄个所需的文件下载,我们需要实现一个委托方法:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
//我们已经成功的完成下载,现在要保存文件
NSFileManage *fileManager = [NSFileManager defauleManager];
NSArray *URLs = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDoma\inMask];
NSURL *dcoumentsDirectory = URLs[0];
NSURL *destinationPath = [documentsDirectory URLByAppendingPathCOmponent:[location lastPathComponent]];
NSError *error;
//确保我们已经覆盖了已经存在的
[fileManager removeItemAtURL:destinationPath error:NULL];
BOOL success = [fileManager copyItemAtURL:location toURL:destinationPath error:&error];
if (success)
{
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithContentOfFile:[destinationPath path]];
self.imageView.image = image;
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
self.imageView.hidden = NO;
});
} else
{
NSLog(@"Couldn't copy the dowloaded file");
}
if (downloadTask == cancelableTask){
cancellableTask = nil;
}
}
这种方法是在NSURLSessionDownloadTaskDelegate定义。我们获得通过下载的文件的临时位置,所以在这段代码中我们保存它关闭的文件目录,然后(因为我们有一个图片)显示给用户。
上述委托方法只被调用,如果下载任务成功完成。下面的方法是NSURLSessionDelegate和被调用每一个任务完成后,不论它是否成功完成。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task dedCompleteWithError:(NSError *)error
{
dispatch_async(dispatch_get_mian_queue(),^{self.progressInicator.hiddent = YES;})
}
如果错误对象为零则任务完成没有问题。否则,有可能对其进行查询,找出问题是什么。如果部分下载已完成,则错误对象包含对一个NSData对象,它可以用来恢复传输在稍后的阶段。
跟踪进度
你会注意到,我们在最后一节的末尾藏进度指示任务完成方法的一部分。此更新进度条的进度再简单不过了。还有就是这是在任务的生命周期被称为零次或多次额外的委托方法:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int62_t)bytesWritten BytesWritten:(int64_t)totalBytesWritten totalByteExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
double currentProgress = totalBytesWritten/(double)totalBytesExpectedToWrite;
dispatch_async(dispatch_get_main_queue() ^{self.progressIndicator.hidden = NO; self.progressIndicator.progress = currentProgress;})
}
这是另一种方法,它是NSURLSessionDownloadTaskDelegate的一部分,我们在这里使用它来
估计进度和更新进度指示器。
退出下载
即使NSURLConnection被取消也不会消失。这是一个简单的能力,与取消NSURLSessionTask不同。
-(IBAction)cancelCancellabel:(id)sender{
if (cancellableTask){
[cancellabelTask cancel];
cancellableTask = nil;
}
}
就是这么简单!值得一提的是UIRLSession:task:didCompleteWithError:delegate方法将被在调用一次后取消,一般你能够适当的更新UI。这很可能取消任务URLSession:downloadTask:didWriteData:BytesWritten:totalBytesExpectedToWrite:方法,可能会被调用,但是didComplete方法可定时最后的。
断点续传下载
它也可以很容易地恢复下载。还存在另一种取消方法,该方法提供了一个NSData对象可以用来创建一个新的任务继续传输在稍后的阶段。如果服务器支持续传然后将数据对象将包括已经下载的内容。
- (IBAction)cancelCancellabel:(id)sender{
if(self.resumableTask){
[self.resumableTask cancelByProducingResumeData:^(NSData *resumeData) { partialDownload = resumeData; self.resumableTask = nil;}];
}
}
在这里,我们已经把恢复数据到ivar,我们以后可以用它来恢复下载。
在创建下载任务,而不是提供你能提供一个恢复数据对象的请求:
if (!self.resumableTask){
if(partialDownload){
self.resumableTask = [inProcessSession downloadTaskWithResumeData:partialDownload];
}else{
NSString *url = @"dizhi";
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
self.resumableTask = [inProcessSession downloadTaskWithResques:request];
}
[self.resumableTask resume];
}
如果我们已经有一个paritialDownload对象,然后创建一个task来使用这个对象,否则我们将像以前一样创建task。唯一一件需要记住的事情就是当process结束我们需要设置 partialDownload = nil;
不想写了,你完全可以参考上面的步骤来完成你的后台下载!