IOS个人开发iOS Developer

iOS文件的断点下载(自定义简单的封装)

2016-10-24  本文已影响117人  Beacon丶

前言:在我们做iOS开发的过程中,常常会有下载文件的需求。我们都知道文件的下载在开发中需求比较频繁,有时候也会用到大文件的断点下载的需求

程序猿.jpg
在iOS9中已经废弃了NSURLConnection的使用,用更为简单的NSURLSession来代替。由于文件的下载我们需要写的代码过于频繁,所以就对文件的下载进行了一下封装,来确保我们之后不必去写那些冗余的代码。

我们先看下效果图:

demo.gif

从图中可以看出,我们开始下载,然后我们杀掉程序,重新打开之后发现可以接着上次的进度继续下载。

接下来我们整理下思路

具体的实现步骤:

-(NSURLSession *)session
{
    if(!_session)
    {
        _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
    }
    return _session;
}
-(NSURLSessionDataTask *)task
{
    if(!_task)
    {
        NSInteger totalLength = [[NSDictionary dictionaryWithContentsOfFile:TotalLengthFilePath][FileName] integerValue];

        if(totalLength && FileInterger == totalLength)
        {
            NSLog(@"文件已经下载过了");
            return nil;
        }

        //创建请求
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:FileURL]];

        //设置请求头
        NSString *range = [NSString stringWithFormat:@"bytes=%zd-", FileInterger];
        [request setValue:range forHTTPHeaderField:@"Range"];

        //创建data任务
        _task = [self.session dataTaskWithRequest:request];
    }

    return _task;
}

//初始化操作
+(instancetype)shareInstance;

//开始下载文件
-(void)xb_download:(NSString *)url progress:(ProgressBlock)progress compeleted:(CompleteBlock)compelete;

//暂停下载文件
-(void)xb_pauseDownload;
/**文件下载的进度*/
@property (nonatomic, copy) ProgressBlock progress;

/**文件完成时候的操作*/
@property (nonatomic, copy) CompleteBlock compelte;

#pragma mark - 下载处理
-(void)xb_download:(NSString *)url progress:(ProgressBlock)progress compeleted:(CompleteBlock)compelete
{
    self.url = url;

    //启动任务
    [self.task resume];

    //赋值
    self.progress = progress;

    self.compelte = compelete;

}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    //打开流
    [self.stream open];

    //获得服务器这次请求返回数据的总长度
    self.currentLength = [response.allHeaderFields[@"Content-Length"] integerValue] + FileInterger;

    //存储总长度
    NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:TotalLengthFilePath];

    if(dict == nil)
    {
        dict = [NSMutableDictionary dictionary];
    }
    dict[FileName] = @(self.currentLength);

    [dict writeToFile:TotalLengthFilePath atomically:YES];

    //接收这个请求,允许接收服务器的数据
    completionHandler(NSURLSessionResponseAllow);
}
/**接收到服务器返回的数据(这个方法被调用的次数可能会有点多)*/
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
    //写入数据
    [self.stream write:data.bytes maxLength:data.length];

    //目前的下载长度
    NSInteger filecurrentLength = [[[NSFileManager defaultManager] attributesOfItemAtPath:CacheFilePath error:nil][NSFileSize] integerValue];

    self.progress(1.0 * filecurrentLength / self.currentLength);

}

/**请求完毕*/
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    //完成调用Block
    self.compelte(error, CacheFilePath);

    //关闭流
    [self.stream close];
    self.stream = nil;

    //清除任务
    self.task = nil;

    self.progress = nil;
}

封装的细节:

//下载文件所需要的URL
#define FileURL self.url

//文件名(沙盒中的文件名)
#define FileName [self md5:FileURL]

//文件的存放路径(cache)
#define CacheFilePath [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:FileName]

//存储文件总长度的存放路径(cache)
#define TotalLengthFilePath [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"totalLength.plist"]

//文件已经下载的长度
#define FileInterger [[[NSFileManager defaultManager] attributesOfItemAtPath:CacheFilePath error:nil][NSFileSize] integerValue]

#pragma mark - 单例的初始化操作
+(instancetype)shareInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        _manger = [[self alloc] init];
    });
    return _manger;
}

+(instancetype)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        _manger = [super allocWithZone:zone];
    });

    return _manger;
}

文件的下载操作,我们平时用到的也不少,但是网上相关的资源比较的少,由于思路比较的简单,具体操作也不是很麻烦,所以就对文件的下载操作进行了封装,大家可以看着借鉴下

最后祝广大的程序员同胞节日快乐

上一篇 下一篇

猜你喜欢

热点阅读