您的位置:首页 > 其它

NSURLConnection使用代理实现跟踪下载进度,分段写入解决下载峰值

2016-01-14 11:17 399 查看
#import "ViewController.h"

@interface ViewController ()<NSURLConnectionDataDelegate>

//所下载文件的总长度
@property (nonatomic,assign) long long expectedContentLength;
//当前下载的进度
@property (nonatomic,assign) long long currentLength;
//接收到的数据
@property (nonatomic,strong) NSMutableData *receiveDate;
@end

@implementation ViewController

- (NSMutableData *)receiveDate{
if (_receiveDate == nil) {
_receiveDate = [NSMutableData data];
}
return _receiveDate;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//1、URL
NSString *urlStr = @"http://127.0.0.1/A01-tableView.mp4";
NSURL *url = [NSURL URLWithString:urlStr];
//2、创建请求
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:1 timeoutInterval:2.0f];
//3、创建连接
NSURLConnection *connect = [NSURLConnection connectionWithRequest:request delegate:self];
//4、启动连接
[connect start];
}

#pragma mark - 代理方法
//1、收到服务器响应会执行这个方法
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{

NSLog(@"文件的大小为:%lld",response.expectedContentLength/1024);
//下载文件的总大小
self.expectedContentLength = response.expectedContentLength;
//将当前下载的长度置零
self.currentLength = 0;

}

//2、收到数据会调用这个方法,下载数据较大时,该方法会被多次调用,data大小有限
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{

//计算当前下载的长度
self.currentLength += data.length;
//计算当前进度
float progress = (float)self.currentLength/self.expectedContentLength;
NSLog(@"当前进度为:%f,线程为:%@",progress,[NSThread currentThread]);

//拼接数据
[self.receiveDate appendData:data];//这一步造成的问题是:随着下载下来的数据越来越大,self.receiveDate占用的内存也越来越大,直到完全下载完毕写入磁盘后才能释放这么大的内存

}

//3、所有数据传输完毕调用该方法
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{

NSLog(@"所有数据下载完毕");

//写入磁盘;
[self.receiveDate writeToFile:@"/Users/fanyong/Desktop/0000.mp4" atomically:YES];

//释放内存
self.receiveDate = nil;

}

//4、下载出现错误调用该方法
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{

NSLog(@"下载失败");
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

@end


从下面截图可以看到,内存峰值最高达到300M,这绝对是不行的。造成这种结果是因为等到全部接收完后才去写入,接收的数据过大,解决办法:接收一点写入一点。



解决办法:在方法

-(void)connection:(NSURLConnection )connection didReceiveData:(NSData )data 中,每次一接收到数据就直接写入到磁盘中,不再拼接数据等全部数据下完,就可以避免内存峰值。

//2、收到数据会调用这个方法,下载数据较大时,该方法会被多次调用,data大小有限
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{

//计算当前下载的长度
self.currentLength += data.length;
//计算当前进度
float progress = (float)self.currentLength/self.expectedContentLength;
NSLog(@"当前进度为:%f,线程为:%@",progress,[NSThread currentThread]);

//    //拼接数据
//    [self.receiveDate appendData:data];//这一步造成的问题是:随着下载下来的数据越来越大,self.receiveDate占用的内存也越来越大,直到完全下载完毕写入磁盘后才能释放这么大的内存

[data writeToFile:self.targetPath atomically:YES];
}


但是这么做并不能得到最终想要的数据,因为方法[data writeToFile:self.targetPath atomically:YES];每次调用都会将之前写入的数据覆盖,所以等所有数据下完会发现只得到了最后一次的data数据。要解决这个问题需要使用文件句柄

修改方法2

//2、收到数据会调用这个方法,下载数据较大时,该方法会被多次调用,data大小有限
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{

//计算当前下载的长度
self.currentLength += data.length;
//计算当前进度
float progress = (float)self.currentLength/self.expectedContentLength;
NSLog(@"当前进度为:%f,线程为:%@",progress,[NSThread currentThread]);

//建立文件句柄,准备写入到目标路径
NSFileHandle *fh = [NSFileHandle fileHandleForWritingAtPath:self.targetPath];

//如果文件不存在,句柄为nil,此时无法操作文件
if (fh == nil) {
//如果当前还没有写入过文件,就先写入一部分
[data writeToFile:self.targetPath atomically:YES];
}else{
//将句柄移到当前文件的末尾
[fh seekToEndOfFile];
//将数据写入(从句柄指向的位置开始写)
[fh writeData:data];
//写完后关闭文件
[fh closeFile];
}
}


这样句柄就会保证每次接收到的data依次拼接在上次接收的data的尾部,而且每次写入后都会关闭文件,防止重复下载的数据拼在一块。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: