美文网首页
转:ios断点续传:NSURLSession和NSURLSes

转:ios断点续传:NSURLSession和NSURLSes

作者: 西门欥雪 | 来源:发表于2016-10-01 22:48 被阅读715次

      苹果提供的NSURLSessionDownloadTask虽然能实现断点续传,但是有些情况是无法处理的,比如程序强制退出或没有调用

    cancelByProducingResumeData取消方法,这时就无法断点续传了。

    使用NSURLSession和NSURLSessionDataTask实现断点续传的过程是:

    1、配置NSMutableURLRequest对象的Range请求头字段信息

    2、创建使用代理的NSURLSession对象

    3、使用NSURLSession对象和NSMutableURLRequest对象创建NSURLSessionDataTask对象,启动任务。

    4、在NSURLSessionDataDelegate的didReceiveData方法中追加获取下载数据到目标文件。

    下面是具体实现,封装了一个续传管理器。可以直接拷贝到你的工程里,也可以参考我提供的DEMO:

    //  MQLResumeManager.h

    //

    //  Created by MQL on 15/10/21.

    //  Copyright © 2015年. All rights reserved.

    //

    #import

    @interfaceMQLResumeManager :NSObject

    /**

    *创建断点续传管理对象,启动下载请求

    *

    *  @param url文件资源地址

    *  @param targetPath文件存放路径

    *  @param success文件下载成功的回调块

    *  @param failure文件下载失败的回调块

    *  @param progress文件下载进度的回调块

    *

    *  @return断点续传管理对象

    *

    */

    +(MQLResumeManager*)resumeManagerWithURL:(NSURL*)url

    targetPath:(NSString*)targetPath

    success:(void(^)())success

    failure:(void(^)(NSError*error))failure

    progress:(void(^)(longlongtotalReceivedContentLength,longlongtotalContentLength))progress;

    /**

    *启动断点续传下载请求

    */

    -(void)start;

    /**

    *取消断点续传下载请求

    */

    -(void)cancel;

    @end

    //

    //  MQLResumeManager.m

    //

    //  Created byMQLon 15/10/21.

    //  Copyright © 2015年. All rights reserved.

    //

    #import"MQLResumeManager.h"

    typedefvoid(^completionBlock)();

    typedefvoid(^progressBlock)();

    @interfaceMQLResumeManager()

    @property(nonatomic,strong)NSURLSession*session;//注意一个session只能有一个请求任务

    @property(nonatomic,readwrite,retain)NSError*error;//请求出错

    @property(nonatomic,readwrite,copy)completionBlockcompletionBlock;

    @property(nonatomic,readwrite,copy)progressBlockprogressBlock;

    @property(nonatomic,strong)NSURL*url;//文件资源地址

    @property(nonatomic,strong)NSString*targetPath;//文件存放路径

    @propertylonglongtotalContentLength;//文件总大小

    @propertylonglongtotalReceivedContentLength;//已下载大小

    /**

    *设置成功、失败回调block

    *

    *  @param success成功回调block

    *  @param failure失败回调block

    */

    - (void)setCompletionBlockWithSuccess:(void(^)())success

    failure:(void(^)(NSError*error))failure;

    /**

    *设置进度回调block

    *

    *  @param progress

    */

    -(void)setProgressBlockWithProgress:(void(^)(longlongtotalReceivedContentLength,longlongtotalContentLength))progress;

    /**

    *获取文件大小

    *  @param path文件路径

    *  @return文件大小

    *

    */

    - (longlong)fileSizeForPath:(NSString*)path;

    @end

    @implementationMQLResumeManager

    /**

    *设置成功、失败回调block

    *

    *  @param success成功回调block

    *  @param failure失败回调block

    */

    - (void)setCompletionBlockWithSuccess:(void(^)())success

    failure:(void(^)(NSError*error))failure{

    __weaktypeof(self) weakSelf =self;

    self.completionBlock= ^ {

    dispatch_async(dispatch_get_main_queue(), ^{

    if(weakSelf.error) {

    if(failure) {

    failure(weakSelf.error);

    }

    }else{

    if(success) {

    success();

    }

    }

    });

    };

    }

    /**

    *设置进度回调block

    *

    *  @param progress

    */

    -(void)setProgressBlockWithProgress:(void(^)(longlongtotalReceivedContentLength,longlongtotalContentLength))progress{

    __weaktypeof(self) weakSelf =self;

    self.progressBlock= ^{

    dispatch_async(dispatch_get_main_queue(), ^{

    progress(weakSelf.totalReceivedContentLength, weakSelf.totalContentLength);

    });

    };

    }

    /**

    *获取文件大小

    *  @param path文件路径

    *  @return文件大小

    *

    */

    - (longlong)fileSizeForPath:(NSString*)path {

    longlongfileSize =0;

    NSFileManager*fileManager = [NSFileManagernew];//

    not thread safe

    if([fileManagerfileExistsAtPath:path]) {

    NSError*error =nil;

    NSDictionary*fileDict = [fileManagerattributesOfItemAtPath:patherror:&error];

    if(!error && fileDict) {

    fileSize = [fileDictfileSize];

    }

    }

    returnfileSize;

    }

    /**

    *创建断点续传管理对象,启动下载请求

    *

    *  @param url文件资源地址

    *  @param targetPath文件存放路径

    *  @param success文件下载成功的回调块

    *  @param failure文件下载失败的回调块

    *  @param progress文件下载进度的回调块

    *

    *  @return断点续传管理对象

    *

    */

    +(MQLResumeManager*)resumeManagerWithURL:(NSURL*)url

    targetPath:(NSString*)targetPath

    success:(void(^)())success

    failure:(void(^)(NSError*error))failure

    progress:(void(^)(longlongtotalReceivedContentLength,longlongtotalContentLength))progress{

    MQLResumeManager*manager = [[MQLResumeManageralloc]init];

    manager.url= url;

    manager.targetPath= targetPath;

    [managersetCompletionBlockWithSuccess:successfailure:failure];

    [managersetProgressBlockWithProgress:progress];

    manager.totalContentLength=0;

    manager.totalReceivedContentLength=0;

    returnmanager;

    }

    /**

    *启动断点续传下载请求

    */

    -(void)start{

    NSMutableURLRequest*request = [[NSMutableURLRequestalloc]initWithURL:self.url];

    longlongdownloadedBytes =self.totalReceivedContentLength= [selffileSizeForPath:self.targetPath];

    if(downloadedBytes >0) {

    NSString*requestRange = [NSStringstringWithFormat:@"bytes=%llu-", downloadedBytes];

    [requestsetValue:requestRangeforHTTPHeaderField:@"Range"];

    }else{

    intfileDescriptor =open([self.targetPathUTF8String],O_CREAT|O_EXCL|O_RDWR,0666);

    if(fileDescriptor >0) {

    close(fileDescriptor);

    }

    }

    NSURLSessionConfiguration*sessionConfiguration = [NSURLSessionConfigurationdefaultSessionConfiguration];

    NSOperationQueue*queue = [[NSOperationQueuealloc]init];

    self.session= [NSURLSessionsessionWithConfiguration:sessionConfigurationdelegate:selfdelegateQueue:queue];

    NSURLSessionDataTask*dataTask = [self.sessiondataTaskWithRequest:request];

    [dataTaskresume];

    }

    /**

    *取消断点续传下载请求

    */

    -(void)cancel{

    if(self.session) {

    [self.sessioninvalidateAndCancel];

    self.session=nil;

    }

    }

    #pragma mark -- NSURLSessionDelegate

    /* The last message a session delegate receives.  A session will only become

    * invalid because of a systemic error or when it has been

    * explicitly invalidated, in which case the error parameter will be nil.

    */

    - (void)URLSession:(NSURLSession*)session didBecomeInvalidWithError:(nullableNSError*)error{

    NSLog(@"didBecomeInvalidWithError");

    }

    #pragma mark -- NSURLSessionTaskDelegate

    /* Sent as the last message related to a specific task.  Error may be

    * nil, which implies that no error occurred and this task is complete.

    */

    - (void)URLSession:(NSURLSession*)session task:(NSURLSessionTask*)task

    didCompleteWithError:(nullableNSError*)error{

    NSLog(@"didCompleteWithError");

    if(error ==nil&&self.error==nil) {

    self.completionBlock();

    }elseif(error !=nil){

    if(error.code!= -999) {

    self.error= error;

    self.completionBlock();

    }

    }elseif(self.error!=nil){

    self.completionBlock();

    }

    }

    #pragma mark -- NSURLSessionDataDelegate

    /* Sent when data is available for the delegate to consume.  It is

    * assumed that the delegate will retain and not copy the data.  As

    * the data may be discontiguous, you should use

    * [NSData enumerateByteRangesUsingBlock:] to access it.

    */

    - (void)URLSession:(NSURLSession*)session dataTask:(NSURLSessionDataTask*)dataTask

    didReceiveData:(NSData*)data{

    //根据status code的不同,做相应的处理

    NSHTTPURLResponse*response = (NSHTTPURLResponse*)dataTask.response;

    if(response.statusCode==200) {

    self.totalContentLength= dataTask.countOfBytesExpectedToReceive;

    }elseif(response.statusCode==206){

    NSString*contentRange = [response.allHeaderFieldsvalueForKey:@"Content-Range"];

    if([contentRangehasPrefix:@"bytes"]) {

    NSArray*bytes = [contentRangecomponentsSeparatedByCharactersInSet:[NSCharacterSetcharacterSetWithCharactersInString:@"

    -/"]];

    if([bytescount] ==4) {

    self.totalContentLength= [[bytesobjectAtIndex:3]longLongValue];

    }

    }

    }elseif(response.statusCode==416){

    NSString*contentRange = [response.allHeaderFieldsvalueForKey:@"Content-Range"];

    if([contentRangehasPrefix:@"bytes"]) {

    NSArray*bytes = [contentRangecomponentsSeparatedByCharactersInSet:[NSCharacterSetcharacterSetWithCharactersInString:@"

    -/"]];

    if([bytescount] ==3) {

    self.totalContentLength= [[bytesobjectAtIndex:2]longLongValue];

    if(self.totalReceivedContentLength==self.totalContentLength)

    {

    //说明已下完

    //更新进度

    self.progressBlock();

    }else{

    //416 Requested Range Not Satisfiable

    self.error= [[NSErroralloc]initWithDomain:[self.urlabsoluteString]code:416userInfo:response.allHeaderFields];

    }

    }

    }

    return;

    }else{

    //其他情况还没发现

    return;

    }

    //向文件追加数据

    NSFileHandle*fileHandle = [NSFileHandlefileHandleForUpdatingAtPath:self.targetPath];

    [fileHandleseekToEndOfFile];//将节点跳到文件的末尾

    [fileHandlewriteData:data];//追加写入数据

    [fileHandlecloseFile];

    //更新进度

    self.totalReceivedContentLength+= data.length;

    self.progressBlock();

    }

    @end

    经验证,如果app后台能运行,datatask是支持后台传输的。

    让您的app成为后台运行app非常简单:

    #import "AppDelegate.h"

    static UIBackgroundTaskIdentifier bgTask;

    @interface AppDelegate ()

    @end

    @implementation AppDelegate

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Override point for customization after application launch.

    return YES;

    }

    - (void)applicationDidEnterBackground:(UIApplication *)application {

    [self getBackgroundTask];

    }

    - (void)applicationWillEnterForeground:(UIApplication *)application {

    [self endBackgroundTask];

    }

    /**

    *  获取后台任务

    */

    -(void)getBackgroundTask{

    NSLog(@"getBackgroundTask");

    UIBackgroundTaskIdentifier tempTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{

    }];

    if (bgTask != UIBackgroundTaskInvalid) {

    [self endBackgroundTask];

    }

    bgTask = tempTask;

    [self performSelector:@selector(getBackgroundTask) withObject:nil afterDelay:120];

    }

    /**

    *  结束后台任务

    */

    -(void)endBackgroundTask{

    [[UIApplication sharedApplication] endBackgroundTask:bgTask];

    bgTask = UIBackgroundTaskInvalid;

    }

    @end

    原文链接:ios断点续传:NSURLSession和NSURLSessionDataTask实现 - qianlima210210的专栏 - 博客频道 - CSDN.NET 

    相关文章

      网友评论

          本文标题: 转:ios断点续传:NSURLSession和NSURLSes

          本文链接:https://www.haomeiwen.com/subject/zlxdyttx.html