zoukankan      html  css  js  c++  java
  • NSURLConnection实现断点续传

      ⾸先来了解下这个东⻄西: Range头域 Range头域可以请求实体的⼀一个或者多个⼦子范围。

    例如, 表⽰示头500个字节:bytes=0-499

    表⽰示第⼆二个500字节:bytes=500-999

    表⽰示最后500个字节:bytes=-500

    表⽰示500字节以后的范围:bytes=500-

    第⼀一个和最后⼀一个字节:bytes=0-0,-1

    同时指定⼏几个范围:bytes=500-600,601-999
    实现断点下载就是在httpRequest中加⼊入 Range 头。
    [request addValue:@"bytes=500-" forHTTPHeaderField:@"Range"];
    ⾄至于能否正常实现断点续传,还要看服务器是否⽀支持。 如果⽀支持,⼀一切没问题。 如果不⽀支持可能出现两种情况,1.不理会你得range值,每次都重
    新下载数据;2.直接下载失败。

    过程如下
    1.下载要保存到本地沙盒中,所以首先要获取沙河中的 全路径,(文件保存在沙盒中Documents/%@)中,目的是获取以进攻下载的文件大小
    MD5加密,是一款数据加密工具,为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。MD5hash,哈希值计算器,是一款md5校验工具。每个文件都可以用Hash MD5验证程序算出一个固定的MD5码来。
    //获取在沙盒中的全路径
    - (NSString *)getFullFilePathWithUrl:(NSString *)urlname {
        NSString * name = [urlname MD5Hash];
        //获取在沙盒中的路径
        NSString *path = [NSHomeDirectory() stringByAppendingFormat:@"/Documents/%@",name];
        return path;
    }
    判断有没有这个文件 有就获取大小  没有就 创建这个文件

    BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
        if (!isExist) {
            self.loadedSize = 0;
            //没有就创建新的空文件
            [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
        }else {
            //有 要获取已经下载的文件大小
            NSDictionary *fileDict = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
            self.loadedSize = fileDict.fileSize;
        }
        //打开文件
        _fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
        
        //发送下载请求
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
        //断点下载 应该 告知 服务器 从哪一字节开始下载
        //通过请求头 发给 服务器 (前提 服务器 必须要支持断点续传)
        //增加请求头 Range头
        [request addValue:[NSString stringWithFormat:@"bytes=%llu-",self.loadedSize] forHTTPHeaderField:@"Range"];
        //启动一个线程 进行请求下载
        _connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

    遵守协议
    #pragma mark - NSURLConnectionDataDelegate
    //服务器给客户端响应
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
        //服务器告知 客户端 要发数据了 还会告诉发送的大小(文件 剩余的大小)
        NSLog(@"len:%llu",response.expectedContentLength);
        //计算文件 总大小
        self.fileSize = self.loadedSize+response.expectedContentLength;
    }
    //服务发送数据的时候 回调
    //一段一段发送
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
        //下载过程 要把data 写到本地沙盒
        [_fileHandle seekToEndOfFile];//定位到文件尾
        [_fileHandle writeData:data];
        [_fileHandle synchronizeFile];//同步到磁盘
        //文件 已经下载大小
        self.loadedSize += data.length;
        
        //回调block 把 大小传给对方
        if (self.myBlock) {
            self.myBlock(self);************Block重点标记在这里传值***************
        }
    }
    停止下载 关闭文件
    - (void)stopDownload {
        if (_connection) {
            [_connection cancel];
            _connection = nil;
        }
        [_fileHandle closeFile];//关闭文件
    }
    view 会负责界面的展示,考虑到界面的刷新 ,会会使用定时器,来控制界面的刷新,
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        //获取比例
        double scale = [[NSUserDefaults standardUserDefaults] doubleForKey:kUrl];
        self.slider.value = scale;
    }

    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    //计算 速度 每隔1s 调用一次
    - (void)getSpeed {
        //当前的已经下载大小 - 前1s 已经下载大小
        self.speed = (self.loadedSize-self.preLoadedSize)/1024.0;
        //保存
        self.preLoadedSize = self.loadedSize;
    }
    - (IBAction)startClick:(id)sender {
        if (!self.timer) {
            self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(getSpeed) userInfo:nil repeats:YES];
        }
        
        __weak typeof(self)weakSelf = self;
        
        [self.download breakPointDownloadDataWithUrl:kUrl downloadProcess:^(BreakPointDownload *download) {***********//block 传值的类型
            //保存已经下载大小
            weakSelf.loadedSize = download.loadedSize;
            
            //下载过程中 会 回调 这个block*************************************
            double fileSize = download.fileSize/1024.0/1024.0;//M
    //使用Block传过来的值*****************************
            double scale = ((double)download.loadedSize)/download.fileSize;
            weakSelf.slider.value = scale;
            
            //把这个进度 保存的本地
            [[NSUserDefaults standardUserDefaults] setDouble:scale forKey:kUrl];
            [[NSUserDefaults standardUserDefaults] synchronize];
            
            if (scale >= 1.0) {
                if (self.timer) {
                    [self.timer invalidate];
                    self.timer = nil;
                }
                //销毁定时器
            }
            weakSelf.label.text = [NSString stringWithFormat:@"已经下载%.2f%% 总大小 %.2fM 速度 %.2fKB/S",scale*100,fileSize,weakSelf.speed];
        }];
    }
    - (IBAction)stopClick:(id)sender {
        if (self.timer) {
            [self.timer invalidate];
            self.timer = nil;
        }
        [self.download stopDownload];
    }
    总结
    1.首先去沙盒中找到文件,打开文件。(_fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath])获取已经下载的文件大小
    2.断点续传,要拼接请求 增加请求头Range 头,让服务器知道从哪里开始下载,所以叫断点续传
    3.开始下载会调用协议方法,并开始通过Block回调,把数据返还给View,
    4.View界面展示数据,通过调用下载方法(含有Block),下载的过程中通过Block返回的值给界面输出数据,
    5.启用定时器,刷新界面,展示数据
    6.停止下载时,首先使定时器销毁,然后 使NSURLConnection取消请求链接([_connection cancel])关闭文件

  • 相关阅读:
    centos 无线网卡安装,网卡rtl8188ee
    centos mysqldb 安装
    centos 安装gcc时,出错:Found 10 pre-existing rpmdb problem(s), 'yum check' output follows:
    peewee 字段属性help_text的支持问题
    mysql查看字段注释(帮助信息)指令
    centos mysql 大量数据导入时1153 错误:1153
    php在centos下的脚本没有解析的问题
    [转]mysql分布式方案-分库拆表
    [转]Mysql海量数据存储和解决方案之一—分布式DB方案
    mysql 数据库字符集的指定
  • 原文地址:https://www.cnblogs.com/ZSongChao001/p/4957297.html
Copyright © 2011-2022 走看看