iOS-Http断点续传
下载LOFTER客户端
IOS Http断点续传浅析
http实现断点续传的关键地方就是在httprequest中加入“Range”头。
//设置Range头,值:bytes=x-y;x:开始字节,y:结束字节,不指定则为文件末尾
[request addValue:@"bytes=500-" forHTTPHeaderField:@"Range"];
如果服务器正确响应的话,就可以顺利续传;如果服务器不支持,那就只能用其它方法了。
经过测试,服务器的不支持分为两种情况:
1.完全没响应
如果不处理会导致文件无法下载。
测试地址:http://dl_dir.qq.com/qqfile/qq/QQforMac/QQ_V2.4.2.dmg
发送请求后,过一段时间直接进入了didFailWithError的delegate;错误信息为time out。
针对这种情况可以做出的处理是:增加一个是否支持断点续传的标志。
具体:
第一次请求,开始字节为0,不用发送Range头,可以正常下载;
当下载中断,开始第二次请求,开始字节不为0,发送range头;
如果进入didFailWithError的delegate,就标明此链接不可以断点续传,每次请求前都清除缓存,保证开始的字节为0,不发送Range头。
2.无论发送Range的值是多少,服务器都会重新下载。
如果不处理,会导致续传过的文件出错。
测试地址:https://github.com/CocoaPods/CocoaPods/archive/master.zip
这种情况的处理方案是:
第一次收到响应的时候,就把文件的总大小记录下来;
以后每次收到响应的时候都比较一下下载长度和总大小是不是一样;
如果一样而且又存在缓存;就表明属于这种情况了;直接删掉缓存,重新下载。
下面是用NSURLConnection实现http断点续传的实例:
针对上面两种做了简单的处理,回调函数还有待添加
MXDownload.h文件:
#import <Foundation/Foundation.h>
@interface MXDownload : NSObject
//文件名路径
@property (nonatomic, readonly) NSString *filePath;
//是否正在下载的标志
@property (nonatomic, readonly) BOOL downloading;
//初始化
- (id)initWithUrlString:(NSString *)urlString;
//两个状态
- (void)start;
- (void)stop;
//清除缓存
- (void)clearCache;
@end
MXDownload.m文件:
#import "MXDownload.h"
#import "NSString+MX.h"
#define FILE_INFO_PLIST [NSString pathWithName:@"MXDownload/fileInfo.plist" directory:NSCachesDirectory]
@interface MXDownload (){
NSURLConnection *_urlConnection;
NSString *_urlString;
BOOL _downloading,_didAddRange,_shouldResume;
NSString *_fileName,*_filePath, *_tempFilePath;
NSFileHandle *_fileHandle;
unsigned long long _fileOffset,_fileSize;
}
@end
@implementation MXDownload
@synthesize downloading = _downloading;
@synthesize filePath = _filePath;
//初始化,顺便设置下载文件和下载临时文件路径
- (id)initWithUrlString:(NSString *)urlString{
self = [super init];
if (self){
_urlString = urlString;
_shouldResume = YES;
if (_urlString) {
_fileName = [_urlString MD5];
_filePath = [NSString pathWithName:[NSString stringWithFormat:@"MXDownload/%@",_fileName] directory:NSCachesDirectory];
_tempFilePath = [NSString stringWithFormat:@"%@.temp",_filePath];
}
}
return self;
}
//开始下载
- (void)start{
//如果正在下载,中断
if (_downloading) return;
//没有url,也中断
if (!_urlString) return;
//临时文件句柄
_fileHandle = [NSFileHandle fileHandleForWritingAtPath:_tempFilePath];
//获取本次请求下载开始的位置,如果文件不存在,就是0
_fileOffset = _fileHandle ? [_fileHandle seekToEndOfFile] : 0;
//初始化请求
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:_urlString]];
//设置缓存策略,很重要,因为文件是自己储存的,和缓存无关,所以要忽略缓存
//要不然第二次请求会出错
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
//最关键地方,设置Range头,值:bytes=x-y;x:开始字节,y:结束字节,不指定则为文件末尾
_didAddRange = NO;
if (_fileOffset != 0 && _shouldResume) {
[request addValue:[NSString stringWithFormat:@"bytes=%llu-",_fileOffset] forHTTPHeaderField:@"Range"];
_didAddRange = YES;
}
_urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[_urlConnection start];
_downloading = YES;
}
//结束下载
- (void)stop{
[_urlConnection cancel];
_urlConnection = nil;
[_fileHandle closeFile];
_downloading = NO;
}
//清除文件
- (void)clearCache{
if (_downloading) [self stop];
[[NSFileManager defaultManager] removeItemAtPath:_filePath error:nil];
[[NSFileManager defaultManager] removeItemAtPath:_tempFilePath error:nil];
}
#pragma mark -
#pragma mark NSURLConnectionDelegate
//接收到响应
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
//本次请求回来的文件大小
long long fileLength = response.expectedContentLength;
if (fileLength == NSURLResponseUnknownLength) [self stop];
NSData *existFileData = [[NSData alloc] initWithContentsOfFile:_filePath];
//检查文件是否已下载完成
if (existFileData && existFileData.length == fileLength) {
NSLog(@"之前已经下载好了");
[self stop];
}
else{
//保存文件的总大小
if (!_didAddRange){
NSMutableDictionary *dic = [NSMutableDictionary new];
[dic addEntriesFromDictionary:[NSDictionary dictionaryWithContentsOfFile:FILE_INFO_PLIST]];
[dic setValue:[NSNumber numberWithLongLong:fileLength] forKey:_fileName];
[dic writeToFile:FILE_INFO_PLIST atomically:YES];
}
NSFileManager *fileManager = [NSFileManager defaultManager];
//先清除掉旧的文件
[fileManager removeItemAtPath:_filePath error:nil];
//如果此次请求回来的大小等于文件的总大小而且临时文件又存在,则删除临时文件
//解决每次请求都是重新开始的问题
NSDictionary *dic = [NSDictionary dictionaryWithContentsOfFile:FILE_INFO_PLIST];
BOOL isTotalLength = fileLength == [[dic valueForKey:_fileName] longLongValue];
if ([fileManager fileExistsAtPath:_tempFilePath] && isTotalLength){
[fileManager removeItemAtPath:_tempFilePath error:nil];
}
//重新创建文件
if (![fileManager fileExistsAtPath:_tempFilePath]){
[fileManager createFileAtPath:_tempFilePath contents:nil attributes:nil];
_fileHandle = [NSFileHandle fileHandleForWritingAtPath:_tempFilePath];
_fileOffset = 0;
}
_fileSize = fileLength + _fileOffset;
//用_fileOffset可以检查是重新下载还是继续下载
NSLog(@"%@",_fileOffset ? @"继续下载" : @"开始下载");
}
}
//不断接收到数据
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)aData{
//写入文件
[_fileHandle writeData:aData];
_fileOffset = [_fileHandle offsetInFile];
NSLog(@"下载进度: %lld / %lld",_fileOffset,_fileSize);
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
[self stop];
//如果不支持续传,删掉临时文件再试一次
if (_shouldResume) {
_shouldResume = NO;
[[NSFileManager defaultManager] removeItemAtPath:_tempFilePath error:nil];
[self start];
}
}
//完成
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
[[NSFileManager defaultManager] moveItemAtPath:_tempFilePath toPath:_filePath error:nil];
NSLog(@"下载完成");
[self stop];
}
@end
调用:
- (IBAction)startDownLoad:(id)sender{
if (_downloader == nil){
// _downloader = [[MXDownload alloc] initWithUrlString:@"https://github.com/CocoaPods/CocoaPods/archive/master.zip"];
_downloader = [[MXDownload alloc] initWithUrlString:@"http://dl_dir.qq.com/qqfile/qq/QQforMac/QQ_V2.4.2.dmg"];
// _downloader = [[MXDownload alloc] initWithUrlString:@"http://192.168.50.19:8080/vcont/wb.mp3"];
}
if (_downloader.downloading) {
[_downloader stop];
}
else{
[_downloader start];
}
}
iOS-Http断点续传的更多相关文章
- 总结iOS开发中的断点续传那些事儿
前言 断点续传概述 断点续传就是从文件赏赐中断的地方重新开始下载或者上传数据,而不是从头文件开始.当下载大文件的时候,如果没有实现断点续传功能,那么每次出现异常或者用户主动的暂停,都会从头下载,这样很 ...
- iOS开发之网络编程--4、NSURLSessionDataTask实现文件下载(离线断点续传下载) <进度值显示优化>
前言:根据前篇<iOS开发之网络编程--2.NSURLSessionDownloadTask文件下载>或者<iOS开发之网络编程--3.NSURLSessionDataTask实现文 ...
- iOS开发之网络编程--3、NSURLSessionDataTask实现文件下载(离线断点续传下载)
前言:使用NSURLSessionDownloadTask满足不这个需要离线断点续传的下载需求,所以这里就需要使用NSURLSessionDataTask的代理方法来处理下载大文件,并且实现离线断点续 ...
- iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载+使用输出流代替文件句柄
前言:本篇讲解,在前篇iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载的基础上,使用输出流代替文件句柄实现大文件断点续传. 在实际开发中,输入输出流用的比较少,但 ...
- iOS 应用开发中的断点续传实践总结
断点续传概述 断点续传就是从文件上次中断的地方开始重新下载或上传数据,而不是从文件开头.(本文的断点续传仅涉及下载,上传不在讨论之内)当下载大文件的时候,如果没有实现断点续传功能,那么每次出现异常或者 ...
- iOS中的下载管理器(支持断点续传)
在空闲时间自己编写了一个简单的iOS下载管理器.该管理器实现如下功能: 1.能够支持正常的下载,暂停,继续操作. 2.支持断点续传,实现暂停执行继续操作后,依然能正常将文件下载完成. 3.实现实时状态 ...
- ios 后台下载,断点续传总结
2018年12月05日 16:09:00 weixin_34101784 阅读数:5 https://blog.csdn.net/weixin_34101784/article/details/875 ...
- iOS 文件下载及断点续传
ios的下载我们可以使用的方法有:NSData.NSURLConnection.NSURLSession还有第三方框架AFNetworking和ASI 利用NSData方法和NSURLConnecti ...
- 数据存储之iOS断点续传
iOS里面实现断点续传 第三方框架之AFN 代码实现 一.iOS里面实现断点续传 1⃣️AFN基于NSURL 1.性能和稳定性略差.针对JSON.XML.Plist和Image四种数据结构封装了各自处 ...
随机推荐
- linux实操_硬盘
1.硬盘分区 硬盘说明: 查看分区和挂载情况 语法: lsblk -f lsblk 2.增加硬盘 (1)虚拟机添加硬盘 (2)分区 fdisk /dev/sdb (3)格式化 mkfs -text4 ...
- python的logging日志模块(一)
最近修改了项目里的logging相关功能,用到了Python标准库里的logging模块,在此做一些记录.主要是从官方文档和stackoverflow上查询到的一些内容. 官方文档 技术博客 基本用法 ...
- HTML - My First Page!
HTML简介 超文本标记语言(英语:HyperText Markup Language,简称:HTML)是一种用于创建网页的标准标记语言. 您可以使用 HTML 来建立自己的 WEB 站点,HTML ...
- SpingMVC入门
Springmvc简介及配置 1. 什么是springMVC? Spring Web MVC是一种基于Java的实现了MVC设计模式的.请求驱动类型的.轻量级Web框架. 2. SpringMVC处理 ...
- 基于ARM的SoC设计入门[转]
原文:基于ARM的SoC设计入门 我们跳过所有对ARM介绍性的描述,直接进入工程师们最关心的问题.要设计一个基于ARM的SoC,我们首先要了解一个基于ARM的SoC的结构.图1是一个典型的SoC的结构 ...
- 敌兵布阵(HDU 1166)
Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任 ...
- Java Collection Framework 备忘点
最顶端是两个接口,集合和映射—— Collection<T> / Map<K, V> List 列表 保持插入顺序 ArrayList 擅长随机读 LinkedList ...
- 如何用 Windows Live Writer 和 Word 2013 分别发表博客到Cnblog 和CSDN
ps CSDN 老是505错误,放弃了 为什么会写这篇 最近写博客在 Cnblog 上面写博客, 发现图片不能复制了直接粘贴上,这对于把博客当随手笔记的人来说无疑非常痛苦.求助于博客园,他们让我用 W ...
- Spark(二)CentOS7.5之Spark2.3.1HA安装
一 下载安装包 1 官方下载 官方下载地址:http://spark.apache.org/downloads.html 2 安装前提 Java8 安装成功 zookeeper 安装成功 had ...
- CentOS6.8安装Docker
在CentOS6.8上安装Docker 1.Docker使用EPEL发布,RHEL系的OS首先要确保已经持有EPEL仓库,否则先检查OS的版本,然后安装相应的EOEL包:如下命令: yum insta ...