说说断点下载

最近项目用到了断点下载,结合着项目,总结一些知识点


一、数据的下载

小到一次普通的get请求,大到一个电影的下载都属于数据的下载。对于数据请求,我们肯定会用到NSURLConnection或者NSURLSession。被普遍使用的AFNetWorking的 AFHTTPRequestOperationManager 是基于 NSURLConnection 的封装,而 AFHTTPSessionManager 则是基于 NSURLSession 的封装

值得一提的而是,NSURLSession 是 iOS7之后苹果推出的新的下载类,明确表示推荐开发者使用。这样一来,NSURLConnection 几乎就没有使用的价值了。


二、断点下载原理

顾名思义,断点下载就是我们能在下载的途中,中断下载任务,再次点击下载的时候,接着上个的数据继续传输,而不是从头开始。

所以,就需要我们在每次下载的时候,在请求头里传入我们已经下载的长度,服务器就会从这个位置接着给我们新的数据。我们需要做的就是接收数据,拼接起来。直到整个文件被下载下来。


三、接收处理数据

这里写图片描述

如图所示,我们一般都不会只是一个下载任务,所以需要一个字典保存所有的任务。


@property (nonatomic, strong) NSMutableDictionary *tasks;//任务字典【用来保存任务,多个任务同时下载,我们要区分开不同的任务,所以用URL作为key】

准守NSURLSessionDownloadDelegate 协议
//下载 供其他页面调用
- (void)downloadFileWithUrl:(NSURL *)url{
    //获取NSURLSession对象 并设置代理
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];

    NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url];

    [self.tasks setValue:task forKey:url];//保存任务
}

需要注意的是对于url里有汉字的,我们需要做一些处理,具体参考URL中文编码

这里需要实现的代理方法有

//服务器有返回数据,就会调用该方法
- (void)URLSession:(NSURLSession *)session downloadTask: (NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    /*
        这里可以计算下载进度
        totalBytesWritten //已经写入本地缓存的大小
        totalBytesExpectedToWrite//文件总的大小
    */ 
}

//任务暂停时调用的方法
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
    /*
        fileOffset:下载任务中止时的偏移量
    */ 
}

//下载发生错误时候调用的方法
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{ 

}

//下载完成的时候调用【下载的过程中,系统会把数据暂时写入沙盒的temp文件中。所以我们要在下载完成的时候把数据剪切到我们自己的文件夹中】
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    //我们自定义保存文件的路径  一般使用服务器返回的文件名作为我们自己的文件名
    NSString *file = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename];

    NSFileManager *fileManager = [NSFileManager defaultManager];
    //将文件剪切到我们自己的路径
    [fileManager moveItemAtURL:location toURL:[NSURL fileURLWithPath:file] error:nil];
}

四、暂停和继续

//点击暂停的时候获取到当前的URL
- (void)suspendOrresumeWithUrl:(NSURL *)url{
    // 根据url ,取出对应得task,继续或者暂停下载
    NSURLSessionDownloadTask *task = self.tasks[url];
    if(task.state == NSURLSessionTaskStateRunning){
        [task suspend];//暂停
    }else if(task.state == NSURLSessionTaskStateSuspended){
        [task resume];//下载
    }
}

五、更新进度条

项目中我使用的是环形进度条,来展示下载的进度。如图
这里写图片描述

#import <UIKit/UIKit.h>

typedef NS_ENUM(NSInteger, DownloadStatus)
{
    Download_Finished = 0,
    Download_Continue,
    Download_Pause,
};


@interface PXSportLoadProgressView : UIView

@property (nonatomic, strong) UIButton *backgroundButton;//暂停和继续下载的按钮
@property (nonatomic, strong) UILabel *centerLabel;//环形中间的进度百分比
@property (nonatomic, strong) UIImageView *centerImageView;//暂停时候的icon

@property (nonatomic, strong) CAShapeLayer *backgroundLayer;//背景layer
@property (nonatomic, strong) CAShapeLayer *progressLayer;//进度layer

//更新进度条
- (void)configProgressViewWithStatus:(DownloadStatus)status andProgress:(CGFloat)progress;

上面就是一个进度条所需要的东西了。下面展示一些关键代码

        self.backgroundLayer = ({
            CGPoint fCirclrCenter = CGPointMake(CGRectGetMidY(self.bounds),CGRectGetMidX(self.bounds));
            CGFloat radius = CGRectGetMidX(self.bounds) - 10;
            CAShapeLayer *backgroundLayer = [CAShapeLayer layer];
            backgroundLayer.path = ([UIBezierPath bezierPathWithArcCenter:fCirclrCenter radius:radius startAngle:M_PI endAngle:-M_PI clockwise:NO]).CGPath;
            backgroundLayer.strokeColor = [UIColor blackColor].CGColor;
            backgroundLayer.fillColor = [UIColor clearColor].CGColor;
            backgroundLayer.lineWidth = 2;
            backgroundLayer;
        });
        [self.layer addSublayer:self.backgroundLayer];

        self.progressLayer = ({
            CAShapeLayer *progressLayer = [CAShapeLayer layer];
            progressLayer.strokeColor = [UIColor px_greenColor].CGColor;
            progressLayer.fillColor = [UIColor clearColor].CGColor;
            progressLayer.lineWidth = 2;
            progressLayer;
        });
        [self.layer addSublayer:self.progressLayer];

上面就是创建了两个layer,我们只需要在接受数据的时候,更新progressLayer即可。

//progress为传过来的进度值。
- (void)updateLoadProgressWithProgress:(CGFloat)progress{

    CGPoint fCirclrCenter = CGPointMake(CGRectGetMidY(self.bounds),CGRectGetMidX(self.bounds));
    CGFloat radius = CGRectGetMidX(self.bounds) - 10;
    self.progressLayer.path = ([UIBezierPath bezierPathWithArcCenter:fCirclrCenter radius:radius startAngle:-M_PI/2 endAngle:(-M_PI/2) + 2*M_PI*progress clockwise:YES]).CGPath;
    self.centerLabel.text = F(@"%.0f%%",progress*100);
    [self.centerLabel setAdjustsFontSizeToFitWidth:YES];
}

需要注意的是progressLayer的开始位置和方向,别搞混了。 顺时针方向,三点钟的方向才是0。我这边进度是以是十二点钟为起点。所以layer的起始位置是-M_PI/2。


以上大致就是断点下载一些重要的知识点,对于一些细节这里不讨论了,项目中如果有问题,大家可以找我一起讨论,相互学习 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值