美文网首页
采用NSURLSession实现断点续传-下载

采用NSURLSession实现断点续传-下载

作者: 小米咸鱼 | 来源:发表于2018-09-07 13:39 被阅读78次

    最近闲暇时间学习了一下断点续传,下面的是采用系统提供的NSURLSession实现的。


    NSURLSession实现方式

    #import <Foundation/Foundation.h>
    
    typedef void (^RSBKPDownloadSizeBlock)(NSURLSessionTask* task,NSProgress* progress);
    typedef void (^RSBKPDownloadComplateBlock)(NSURLSessionTask* task,NSError* error);
    
    
    /**
     网络请求 断点续传
     
     */
    @interface RSNetWorkRequestBKPDownload : NSObject
    
    
    /**
     断点续传-下载文件 (自动开启下载,当文件被下载完成后,再起下载该文件,且保存路径不变时,会调用文件下载完成,error为canceled)
     
     @param url 文件全路径地址 - http://......
     @param path 保存文件路径(断点下载与保存路径相关)
     @return 下载任务
     */
    + (NSURLSessionDataTask *)RS_DownloadURL:(NSString *)url toPath:(NSString *)path receiveResponseBlock:(RSBKPDownloadSizeBlock)receiveResponseBlock receiveDataBlock:(RSBKPDownloadSizeBlock)receiveDataBlock completeBlock:(RSBKPDownloadComplateBlock)downComplateBlock;
    
    /*---------------------------重启--------------------------------*/
    + (void)RS_ResumeTask:(NSURLSessionTask*)task;
    + (void)RS_ResumeTaskForURL:(NSString*)url;
    + (void)RS_ResumeAllTasks;
    
    /*-----------------------------暂停------------------------------*/
    + (void)RS_SuspendTask:(NSURLSessionTask*)task;
    + (void)RS_SuspendTaskForURL:(NSString*)url;
    + (void)RS_SuspendAllTasks;
    
    /*----------------------------取消-------------------------------*/
    + (void)RS_CancelTask:(NSURLSessionTask*)task;
    + (void)RS_CancelTaskForURL:(NSString*)url;
    + (void)RS_CancelAllTask;
    
    
    /**
     销毁session,防止session对d其代理的强引用,而造成内存泄漏
     */
    + (void)RS_invalidate;
    @end
    
    
    #import "RSNetWorkRequestBKPDownload.h"
    #import "NSString+String.h"
    
    /**
     下载信息模型
     */
    @interface RSBKPModel:NSObject
    
    @property(nonatomic,copy) NSString*                     url;
    @property(nonatomic,copy) NSString*                     savePath;
    @property(nonatomic,copy) RSBKPDownloadSizeBlock        receiveResponseBlock;
    @property(nonatomic,copy) RSBKPDownloadSizeBlock        receiveDataBlock;
    @property(nonatomic,copy) RSBKPDownloadComplateBlock    downComplateBlock;
    @property(nonatomic,strong) NSFileHandle*               fileHandle;
    @property(nonatomic,strong) NSProgress*                 progress;
    @end
    
    @implementation RSBKPModel
    
    @end
    
    @interface RSNetWorkRequestBKPDownload()<NSURLSessionDataDelegate>
    
    @property(nonatomic,strong) NSMutableDictionary*        downloadInfoData;
    @property(nonatomic,strong) NSMutableDictionary*        downLoadingTasks;
    @property(nonatomic,strong) NSURLSession*               session;
    @end
    
    //static NSURLSession*            session;
    @implementation RSNetWorkRequestBKPDownload
    
    + (instancetype)share{
        static RSNetWorkRequestBKPDownload* BKPDownload;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            BKPDownload = [RSNetWorkRequestBKPDownload new];
        });
        return BKPDownload;
    }
    
    + (NSURLSessionDataTask*)RS_DownloadURL:(NSString *)url toPath:(NSString *)path receiveResponseBlock:(RSBKPDownloadSizeBlock)receiveResponseBlock receiveDataBlock:(RSBKPDownloadSizeBlock)receiveDataBlock completeBlock:(RSBKPDownloadComplateBlock)downComplateBlock{
        return [[self share] _RS_downloadURL:url toPath:path receiveResponseBlock:receiveResponseBlock receiveDataBlock:receiveDataBlock completeBlock:downComplateBlock];
    }
    
    + (void)RS_ResumeTask:(NSURLSessionDataTask*)task{
        if (task && task.state == NSURLSessionTaskStateSuspended){
            [task resume];
        }
    }
    
    + (void)RS_ResumeTaskForURL:(NSString*)url{
        NSURLSessionTask* task = [[self share] downLoadingTasks][url.MD5];
        [self RS_ResumeTask:task];
    }
    
    + (void)RS_ResumeAllTasks{
        [[[self share] downLoadingTasks].allValues enumerateObjectsUsingBlock:^(NSURLSessionTask*  _Nonnull task, NSUInteger idx, BOOL * _Nonnull stop) {
            [self RS_ResumeTask:task];
        }];
    }
    
    + (void)RS_SuspendTask:(NSURLSessionDataTask*)task{
        if (task && task.state == NSURLSessionTaskStateRunning){
            [task suspend];
        }
    }
    
    + (void)RS_SuspendTaskForURL:(NSString*)url{
        NSURLSessionTask* task = [[self share] downLoadingTasks][url.MD5];
        [self RS_SuspendTask:task];
    }
    
    + (void)RS_SuspendAllTasks{
        [[[self share] downLoadingTasks].allValues enumerateObjectsUsingBlock:^(NSURLSessionTask*  _Nonnull task, NSUInteger idx, BOOL * _Nonnull stop) {
            [self RS_SuspendTask:task];
        }];
    }
    
    + (void)RS_CancelTask:(NSURLSessionTask*)task{
        if (task && (task.state == NSURLSessionTaskStateRunning || task.state == NSURLSessionTaskStateSuspended)){
            [task cancel];
        }
    }
    
    + (void)RS_CancelTaskForURL:(NSString*)url{
        NSURLSessionTask* task = [[self share] downLoadingTasks][url.MD5];
        [self RS_CancelTask:task];
    }
    
    + (void)RS_CancelAllTask{
        [[[self share] downLoadingTasks].allValues enumerateObjectsUsingBlock:^(NSURLSessionTask*  _Nonnull task, NSUInteger idx, BOOL * _Nonnull stop) {
            [self RS_CancelTask:task];
        }];
    }
    + (void)RS_invalidate{
        [[[RSNetWorkRequestBKPDownload share] session] invalidateAndCancel];
        [RSNetWorkRequestBKPDownload share].session = nil;
    }
    
    
    #pragma mark - private
    - (NSURLSessionDataTask *)_RS_downloadURL:(NSString *)url toPath:(NSString *)path receiveResponseBlock:(RSBKPDownloadSizeBlock)receiveResponseBlock receiveDataBlock:(RSBKPDownloadSizeBlock)receiveDataBlock completeBlock:(RSBKPDownloadComplateBlock)downComplateBlock{
        NSURLSessionDataTask* urlTask = self.downLoadingTasks[url.MD5];
        if (urlTask) {
            return urlTask;
        }
        NSFileManager* fileManager = [NSFileManager defaultManager];
        if (![fileManager fileExistsAtPath:path]) {
            [fileManager createFileAtPath:path contents:nil attributes:nil];
        }
    
        NSError* error;
        NSDictionary* fileInfo = [fileManager attributesOfItemAtPath:path error:&error];
        if (error) {
            if (downComplateBlock) {
                downComplateBlock(nil,error);
            }
            return nil;
        }
        
        NSFileHandle* fileHandle = [NSFileHandle fileHandleForWritingAtPath:path];
        unsigned long long location = fileInfo.fileSize;
        
        NSURLRequest* reuqest = [self _RS_initDownloadRequest:url withBytesLength:location];
        NSURLSessionDataTask* task = [self.session dataTaskWithRequest:reuqest];
        //创建model保存相关信息
        NSProgress* progress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
        progress.completedUnitCount = location;
        RSBKPModel* model = [RSBKPModel new];
        model.url = url;
        model.savePath = path;
        model.receiveResponseBlock = receiveResponseBlock;
        model.receiveDataBlock = receiveDataBlock;
        model.downComplateBlock = downComplateBlock;
        model.fileHandle = fileHandle;
        model.progress = progress;
        //缓存model
        [self.downloadInfoData setObject:model forKey:@(task.taskIdentifier)];
        //将任务标记为正在运行
        [self.downLoadingTasks setObject:task forKey:url.MD5];
        [task resume];
        return task;
    }
    
    - (NSURLRequest*)_RS_initDownloadRequest:(NSString*)url withBytesLength:(unsigned long long)length{
        NSURL* reqUrl = [NSURL URLWithString:url];
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:reqUrl];
        // 设置HTTP请求头中的Range
        NSString* range = [NSString stringWithFormat:@"bytes=%llu-", length];
        [request setValue:range forHTTPHeaderField:@"Range"];
        return request;
    }
    
    #pragma mark - NSULRSessionDataDelegate
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
        RSBKPModel* currentModel = self.downloadInfoData[@(dataTask.taskIdentifier)];
        if (response.expectedContentLength > currentModel.progress.completedUnitCount) {
            currentModel.progress.totalUnitCount = response.expectedContentLength + currentModel.progress.completedUnitCount;
            if (currentModel.receiveResponseBlock) {
                currentModel.receiveResponseBlock(dataTask, currentModel.progress);
            }
            completionHandler(NSURLSessionResponseAllow);
        }else{
            completionHandler(NSURLSessionResponseCancel);
        }
    }
    
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
        RSBKPModel* currentModel = self.downloadInfoData[@(dataTask.taskIdentifier)];
        // 指定数据的写入位置 -- 文件内容的最后面
        [currentModel.fileHandle seekToEndOfFile];
        // 向沙盒写入数据
        [currentModel.fileHandle writeData:data];
        currentModel.progress.completedUnitCount += data.length;
        if (currentModel.receiveDataBlock) {
            currentModel.receiveDataBlock(dataTask, currentModel.progress);
        }
    }
    
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
        RSBKPModel* currentModel = self.downloadInfoData[@(task.taskIdentifier)];
        [currentModel.fileHandle closeFile];
        [self.downloadInfoData removeObjectForKey:task];
        [self.downLoadingTasks removeObjectForKey:currentModel.url.MD5];
        if (currentModel.downComplateBlock) {
            currentModel.downComplateBlock(task,error);
        }
    }
    
    #pragma mark - getter
    - (NSURLSession *)session{
        if (!_session) {
            _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
        }
        return _session;
    }
    
    - (NSMutableDictionary *)downloadInfoData{
        if (!_downloadInfoData) {
            _downloadInfoData = @{}.mutableCopy;
        }
        return _downloadInfoData;
    }
    
    /**
     保存正在进行的下载任务
    
     @return 字典
     */
    - (NSMutableDictionary*)downLoadingTasks{
        if (!_downLoadingTasks) {
            _downLoadingTasks = @{}.mutableCopy;
        }
        return _downLoadingTasks;
    }
    @end
    
    

    相关文章

      网友评论

          本文标题:采用NSURLSession实现断点续传-下载

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