zoukankan      html  css  js  c++  java
  • 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. }


  • 相关阅读:
    Jmeter接口自动化-5-提取JSON响应中数组的长度
    Redis系列讲解
    jQuery.Autocomplete实现自动完成功能(详解)
    js中获得当前时间是年份和月份
    搭建SSH框架所需Jar包及其解释
    JS中把字符串转成JSON对象的方法
    JBPM数据库表说明
    java的System.getProperty()方法可以获取的值
    Mybatis3.2.3+mysql第一个例子(入门)
    多线程学习
  • 原文地址:https://www.cnblogs.com/Xfsrn/p/5000333.html
Copyright © 2011-2022 走看看