码迷,mamicode.com
首页 > 移动开发 > 详细

IOS断点下载

时间:2015-09-16 12:17:47      阅读:303      评论:0      收藏:0      [点我收藏+]

标签:

下载的一些用法

①对于小文件,可以直接下载,无需断点下载等处理。

 1 -(void)clickDownBtn{
 2     NSURL *url = [NSURL URLWithString:@"https://picjumbo.imgix.net/HNCK8461.jpg?q=40&w=1650&sharp=30"];
 3     if (self.imgView.image == nil) {
 4         [self downLoad:url];
 5     } 
 6 }
 7 
 8 //下载过程
 9 -(void)downLoad:(NSURL *)url{
10     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
11         NSData *imgData = [NSData dataWithContentsOfURL:url];
12         UIImage *img = [UIImage imageWithData:imgData];
13         if (imgData != nil) {
14             dispatch_sync(dispatch_get_main_queue(), ^{
15                 self.imgView.image = img;
16             });
17         }
18     });
19 }

需要注意的是,多线程中,数据处理是在子线程,UI更新是在主线程。若处理数据时没有在子线程中进行,那么会发生线程阻塞、界面卡死的情况。如下载图片时,

-(void)downLoad:(NSURL *)url{
    //数据处理(NSData)是在主线程中进行,所以会卡死
    //况且这种写法是没有意义的,本来就在主线程,就无需再跳往主线程更新UI
    NSData *imgData = [NSData dataWithContentsOfURL:url];
    UIImage *img = [UIImage imageWithData:imgData];
    if (imgData != nil) {
        dispatch_sync(dispatch_get_main_queue(), ^{
            self.imgView.image = img;
        });
    }
}

正确的方法是,先开辟子线程,然后在主线程更新UI。

开辟子线程

-(void)downLoad:(NSURL *)url{
    //开辟子线程
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //操作
    });
}

async是异步,sync是同步

 

②而对于大文件,则可以使用NSURLSession实现暂停、断点下载等操作。如下:
监听下载进度,需要实现代理NSURLSessionDownloadDelegate,在这代理中常用的有3个方法

/** 下载完毕会调用,location为文件临时地址 */
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
    
}
/**
 *  每次写入沙盒完毕后调用,在这里可以设置进度条数据等操作
 *  totalBytesWritten/totalBytesExpectedToWrite,这两个用来监视下载进度
 *
 *  @param bytesWritten              这次写入的大小
 *  @param totalBytesWritten         已经写入沙盒的大小
 *  @param totalBytesExpectedToWrite 文件总大小
 */
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask               didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
    
}
/** 恢复下载时使用 */
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{
    
}

示例代码如下,在.m中

/** 展示进度的label */
@property(nonatomic,strong) UILabel *progressLabel;
/** 进度动画 */
@property(nonatomic,strong) UIProgressView *progressView;
/** 下载按钮 */
@property(nonatomic,strong) UIButton *downBtn;
/** 下载任务 */
@property(nonatomic,strong) NSURLSessionDownloadTask *downLoadTask;
/** 缓存数据 */
@property(nonatomic,strong) NSData *tempData;
/** session */
@property(nonatomic,strong) NSURLSession *session;

session的懒加载为

-(NSURLSession *)session{
    if (!_session) {
        NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration];
        _session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}

按钮点击事件如下:

//点击下载按钮
-(void)clickDownBtn{
    self.downBtn.selected = !self.downBtn.selected;
    if (nil == self.downLoadTask) {
        if (self.tempData) {
            [self goOnDownload]; //继续下载
        } else { //从0开始下载
            [self startDownload];
        }
    } else {
        [self pauseDownload];
    }
}

/** 开始下载 */
-(void)startDownload{
    NSURL *url = [NSURL URLWithString:@"http://11.gxdx2.crsky.com/201501/apkok-v3.0.zip"];
    
    //创建任务
    self.downLoadTask = [self.session downloadTaskWithURL:url];
    //开始任务
    [self.downLoadTask resume];
}

/** 暂停下载 */
-(void)pauseDownload{
    //防止循环引用
    __weak typeof(self) weakSelf = self;
    [self.downLoadTask cancelByProducingResumeData:^(NSData *resumeData) {
        //resumeData包含了继续下载的开始位置和url
        weakSelf.tempData = resumeData;
        weakSelf.downLoadTask = nil;
    }];
}

/** 恢复下载 */
-(void)goOnDownload{
    //传入上次暂停下载返回的数据,即可恢复下载
    self.downLoadTask = [self.session downloadTaskWithResumeData:self.tempData];
    [self.downLoadTask resume];
    self.tempData = nil;
}

实现2个代理方法,第3个暂时用不到

/** 下载完毕会调用,location为文件临时地址 */
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
    //选择下载文件路径,文件只能下载到该程序的目录中
    //下边为下载到Documents文件夹,若将NSDocumentDirectory换为NSCachesDirectory,则会下载到Cache文件夹
    NSString *caches = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    //response.suggestedFilename建议使用的文件名,一般跟服务器端的名称一致
    NSString *file = [caches stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
    //将临时文件复制或剪切到caches文件夹
    NSFileManager *fileManager = [NSFileManager defaultManager];
    //AtPath : 剪切前的文件路径
    //toPath : 剪切后的文件路径
    //因为文件都是下载到临时文件夹,下载完成后会删除,所以必须移位置
    [fileManager moveItemAtPath:location.path toPath:file error:nil];
   
    // 提示下载完成
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"下载完成" message:downloadTask.response.suggestedFilename delegate:self cancelButtonTitle:nil otherButtonTitles: nil];
    [alert show];
    [self performSelector:@selector(dismissAlert:) withObject:alert afterDelay:2];
    
    //将下载按钮置灰
    self.downBtn.selected = NO;
    self.downBtn.enabled = NO;
}

/**
 *  每次写入沙盒完毕后调用,在这里可以设置进度条数据等操作
 *  totalBytesWritten/totalBytesExpectedToWrite,这两个用来监视下载进度
 *
 *  @param bytesWritten              这次写入的大小
 *  @param totalBytesWritten         已经写入沙盒的大小
 *  @param totalBytesExpectedToWrite 文件总大小
 */
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask               didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
    //注意该处的progress,等号右边必须强制转换为double型,进度条才有动画效果,否则只有在下载完成后才有
    self.progressView.progress = (float)totalBytesWritten/totalBytesExpectedToWrite;
    
    //设置成百分比形式
    NSString *percent = [NSString stringWithFormat:@"下载进度 %0.0f%%", (double)totalBytesWritten/totalBytesExpectedToWrite*100];
    self.progressLabel.text = percent;
}

其中一些点要注意下:
一、
UIProgressView的progress为float型,所以设置时也要为float型,否则不显示动画效果

self.progressView.progress = (float)totalBytesWritten/totalBytesExpectedToWrite;

效果而下:

技术分享

没有强制转换

self.progressView.progress = totalBytesWritten/totalBytesExpectedToWrite;

效果而下:

技术分享

二、
NSSearchPathForDirectoriesInDomains可以用来获取app的私有文件路径,即沙盒中的文件夹列表,获取的为数组形式。

//获取Document文件夹
NSString *document = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
//获取Cache文件夹
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];

 

IOS断点下载

标签:

原文地址:http://www.cnblogs.com/Apologize/p/4812703.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!