iOS开发网络多线程之断点下载


一. 断点下载文件

1. 实现功能

点击"开始下载" -> 开始下载文件,进度条实时变化

点击"暂停下载" -> 暂停下载,进度条停止走动

点击"恢复下载" -> 接着上一次下载进度下载

将正在下载或在暂停没有下载完时,将APP关闭,在重新打开时,进度条现在之前的下载进度,点击开始下载,接着上次下载


2. 思路

    1> 要实现断点下载,需要将已下载文件存放到cache缓存中,且把文件总大小存放到cache缓存中,程序每次启动,获取沙盒中已下载文件的大小和文件中大小,拿出这两个值之后就可以设置进度条的进度

    2> 点击开始按钮: 

  • 创建一个NSURLSession会话对象,设置代理

  • 创建一个NSURLSessionDataTask任务发送请求,创建请求时设置请求头部信息,声明文件从哪里开始下载

    3> 实现代理方法

  • 在接收服务器响应方法中获取到当前下载文件的大小,并以字典形式写入到沙盒缓存中,创建输出流用来追加写入数据到沙盒中

  • 在接收数据方法中,输出流对象调用写入方法将接收到的数据写入到沙盒中

  • 在完成数据接收方法中,将关闭输出流对象,并将其指针为nil

3. 实现代码

1> 点击开始,开启任务

  1. // 开始下载
  2. - (IBAction)start {
  3. [self.dataTask resume];
  4. }

2> 点击暂停,暂停下载

  1. // 暂停下载
  2. - (IBAction)suspond {
  3. [self.dataTask suspend];
  4. }

3> 点击恢复,恢复下载

  1. // 恢复下载
  2. - (IBAction)recover {
  3. [self.dataTask resume];
  4. }

4> 创建session会话,并设置代理

  1. - (NSURLSession *)session
  2. {
  3. if (_session == nil) {
  4. // 1.创建session会话
  5. _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
  6. }
  7. return _session;
  8. }

5> 创建NSURLSessionDataTask任务

请求头部信息中声明从哪里开始下载

  1. - (NSURLSessionDataTask *)dataTask
  2. {
  3. if (_dataTask == nil) {
  4. // 下载完成直接返回,不发送网络请求
  5. NSLog(@"curSize = %zd --- %zd", self.curSize, [self getTotalSize]);
  6. if (self.curSize == [self getTotalSize] && self.curSize != 0) {
  7. NSLog(@"下载完成");
  8. return nil;
  9. }
  10. // 2.创建dataTask任务
  11. /*
  12. 表示头500个字节:Range: bytes=0-499
  13. 表示第二个500字节:Range: bytes=500-999
  14. 表示最后500个字节:Range: bytes=-500
  15. 表示500字节以后的范围:Range: bytes=500-
  16. */
  17. NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_04.mp4"];
  18. // 创建请求
  19. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
  20. NSString *range = [NSString stringWithFormat:@"bytes=%zd-", self.curSize];
  21. [request setValue:range forHTTPHeaderField:@"Range"];
  22. _dataTask = [self.session dataTaskWithRequest:request];
  23. }
  24. return _dataTask;
  25. }

6> 代理方法实现

  • 在接收服务器响应方法中获取到当前下载文件的大小,并以字典形式写入到沙盒缓存中,创建输出流用来追加写入数据到沙盒中

  1. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
  2. {
  3. // 如何处理服务器返回的数据
  4. completionHandler(NSURLSessionResponseAllow);
  5. self.totalSize = response.expectedContentLength + self.curSize;
  6. // 将文件数据的大小保存到沙盒中
  7. // 取出沙盒中保存数据大小的字典
  8. NSString *totalSizePath = [NSString stringGetCacheFullPathToWithFileName:KtotalSize];
  9. NSMutableDictionary *dictM = [NSMutableDictionary dictionaryWithContentsOfFile:totalSizePath];
  10. if (dictM == nil) { // 如果没有初始化字典
  11. dictM = [NSMutableDictionary dictionary];
  12. }
  13. // 给字典赋值
  14. dictM[KfileName] = @(self.totalSize);
  15. // 再将字典回写到沙盒中
  16. [dictM writeToFile:totalSizePath atomically:YES];
  17. // 拼接沙盒路径
  18. NSString *filePath = [NSString stringGetCacheFullPathToWithFileName:KfileName];
  19. self.filePath = filePath;
  20. // 创建输出流
  21. NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:filePath append:YES];
  22. self.stream = stream;
  23. [stream open];
  24. }
  • 在接收数据方法中,输出流对象调用写入方法将接收到的数据写入到沙盒中

  1. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
  2. {
  3. NSLog(@"didReceiveData");
  4. NSLog(@"%zd", data.length);
  5. // 已下载数据的大小
  6. self.curSize += data.length;
  7. // 下载进度
  8. self.progressView.progress = 1.0 * self.curSize / self.totalSize;
  9. self.label.text = [NSString stringWithFormat:@"%f", 1.0 * self.curSize / self.totalSize];
  10. // 数据存放到本地
  11. [self.stream write:data.bytes maxLength:data.length];
  12. }
  • 在完成数据接收方法中,将关闭输出流对象,并将其指针为nil

  1. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
  2. {
  3. NSLog(@"didCompleteWithError");
  4. [self.stream close];
  5. self.stream = nil;
  6. }

7> 程序关闭在打开现在当前进度

  1. - (void)viewDidLoad {
  2. [super viewDidLoad];
  3. // 1.获取沙盒中已下载文件的大小
  4. NSString *filePath = [NSString stringGetCacheFullPathToWithFileName:KfileName];
  5. NSLog(@"%@", filePath);
  6. NSFileManager *manager = [NSFileManager defaultManager];
  7. NSDictionary *fileDict = [manager attributesOfItemAtPath:filePath error:nil];
  8. self.curSize = [fileDict[@"NSFileSize"] integerValue];
  9. NSLog(@"%zd", self.curSize);
  10. // 2.获取沙盒中数据总大小
  11. self.totalSize = [self getTotalSize];
  12. if (self.totalSize == 0) {
  13. NSLog(@"第一次下载");
  14. } else {
  15. self.progressView.progress = 1.0 * self.curSize / self.totalSize;
  16. }
  17. }

获取沙盒中文件总大小

  1. // 获取沙盒中文件总大小
  2. - (NSInteger)getTotalSize
  3. {
  4. NSString *totalSizePath = [NSString stringGetCacheFullPathToWithFileName:KtotalSize];
  5. NSLog(@"%@", totalSizePath);
  6. NSMutableDictionary *dictM = [NSMutableDictionary dictionaryWithContentsOfFile:totalSizePath];
  7. NSLog(@"%@", dictM);
  8. if (dictM == nil) {
  9. return 0;
  10. } else {
  11. NSLog(@"------%zd", [dictM[KfileName] integerValue]);
  12. return [dictM[KfileName] integerValue];
  13. }
  14. }

8> 凡创建的会话都需要在dealloc中释放内存

  1. - (void)dealloc
  2. {
  3. // 会话释放
  4. [self.session invalidateAndCancel];
  5. }


posted @ 2015-11-27 12:49  文刂Rn  阅读(235)  评论(0编辑  收藏  举报