您的位置:首页 > 理论基础 > 计算机网络

iOS网络-通过NSURLConnection文件下载,断点下载,输出流下载

2016-01-17 11:44 525 查看
小文件下载
NSURLConnection-delegate实现,可以监听到下载的进度,但是还是无法解决内存飙升的问题,所以仅限小文件下载

@interface ViewController ()<NSURLConnectionDataDelegate>
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
/** 接收数据的data */
@property (nonatomic, strong) NSMutableData *fileData; /*文件名称 */
@property (nonatomic, strong) NSString *fileName;
@property (nonatomic, assign) NSInteger totalSize;
@end
@implementation ViewController
-(NSMutableData *)fileData
{
if (_fileData == nil) {
_fileData = [NSMutableData data];
}
return _fileData;
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//1.确定URL
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
//2.创建请求对象
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url];
//3.设置代理,发送请求
[NSURLConnection connectionWithRequest:request delegate:self];
}
#pragma mark - NSURLConnectionDataDelegate
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//suggestedFilename:服务器端推荐的名称,其实就是url的最后一个节点
self.fileName = response.suggestedFilename;
//expectedContentLength:获得文件总大小

self.totalSize = response.expectedContentLength;
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//拼接数据
[self.fileData appendData:data];
//计算文件的下载进度== 已经下载的大小/文件的总大小
NSLog(@"%f",1.0 * self.fileData.length /self.totalSize);
//进度UI     self.progressView.progress = 1.0 * self.fileData.length /self.totalSize;
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//写数据到磁盘
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//拼接全路径
NSString *fullPath = [caches stringByAppendingPathComponent:self.fileName];
//写沙盒
[self.fileData writeToFile:fullPath atomically:YES];
}
@end


大文件下载
NSURLConnection-delegate实现, 实现思路:
边接收数据边写文件以解决内存越来越大的问题


@interface ViewController ()<NSURLConnectionDataDelegate>
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;

/*文件名称 */
@property (nonatomic, strong) NSString *fileName;
/*文件的总大小*/
@property (nonatomic, assign) NSInteger totalSize;
/*已经下载的文件大小*/
@property (nonatomic, assign) NSInteger currentSize;
/** 文件句柄 */
@property (nonatomic, strong) NSFileHandle *handle;
@end

@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//1.确定URL
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
//2.创建请求对象
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url];
//3.设置代理,发送请求
[NSURLConnection connectionWithRequest:request delegate:self];

}
#pragma mark - NSURLConnectionDataDelegate
//接收到服务器的响应
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//1.获得caches目录
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//2.拼接全路径
NSString *fullPath = [caches stringByAppendingPathComponent:response.suggestedFilename];
//3.获得本次请求的文件数据大小(期望大小)
self.totalSize = response.expectedContentLength;
//4.新建一个空的文件
/*
第一个参数:文件的目录
第二个参数:文件的内容
第三个参数:文件的属性
*/
[[NSFileManager defaultManager]createFileAtPath:fullPath contents:nil attributes:nil];
//5.创建文件句柄
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:fullPath];
self.handle = handle;
}

//接收到服务器返回的数据
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//1.移动文件句柄到文件的末尾
[self.handle seekToEndOfFile];
//2.写数据到磁盘
[self.handle writeData:data];
//3.计算文件的下载进度== 已经下载的大小/文件的总大小
self.currentSize +=data.length;
NSLog(@"%f",1.0 * self.currentSize /self.totalSize);
//4.进度UI
self.progressView.progress = 1.0 * self.currentSize /self.totalSize;
}
//请求完成
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//释放文件句柄
[self.handle closeFile];
self.handle = nil;
}
@end


大文件断点下载
实现思路: 设置请求头NSFileHandle
在下载文件的时候不再是整块的从头开始下载,而是看当前文件已经下载到哪个地方,然后从该地方接着往后面下载。可以通过在请求对象中设置请求头实现。

1.设置请求头,在开始下载的时候设置

//2.创建请求对象 可变的
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//2.1 设置下载文件的某一部分
// 只要设置HTTP请求头的Range属性, 就可以实现从指定位置开始下载
/*
表示头500个字节:Range: bytes=0-499
表示第二个500字节:Range: bytes=500-999
表示最后500个字节:Range: bytes=-500
表示500字节以后的范围:Range: bytes=500-
*/
NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentLength];
[request setValue:range forHTTPHeaderField:@"Range"];


2.注意点(下载进度并判断是否需要重新创建文件)

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response接收到服务器的响应的方法中代码改动:

//注意点:[code]response
.expectedContentLength获得是本次请求要下载的文件的大小(并非是完整的文件的大小)
//文件的总大小 == 本次要下载的文件大小 + 已经下载的文件的大小
self.totalLength = response.expectedContentLength + self.currentLength;

// 判断当前是否已经下载过,如果当前文件已经存在,那么直接返回,不然新下载的大小会覆盖原来文件,导致前面文件丢失
if (self.currentLength >0) {
return;
}[/code]

大文件输出流下载
使用输出流也可以实现和NSFileHandle相同的功能

//1.创建一个数据输出流
/*
第一个参数:二进制的流数据要写入到哪里
第二个参数:采用什么样的方式写入流数据,如果YES则表示追加,如果是NO则表示覆盖
*/
NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:fullPath append:YES];

//只要调用了该方法就会往文件中写数据
//如果文件不存在,那么会自动的创建一个
[stream open];
self.stream = stream;

//2.当接收到数据的时候写数据
//使用输出流写数据
/*
第一个参数:要写入的二进制数据
第二个参数:要写入的数据的大小
*/
[self.stream write:data.bytes maxLength:data.length];

//3.当文件下载完毕的时候关闭输出流
//关闭输出流
[self.stream close];
self.stream = nil;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: