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])关闭文件

原文地址:https://www.cnblogs.com/ZSongChao001/p/4957297.html