您的位置:首页 > 其它

关于-大文件下载

2016-09-01 10:38 162 查看

NSURLConnection下载

NSURLConnection还提供了另外一种发送请求的方式

//发送请求去下载(创建完conn对象后,会自动发起一个异步请求)
[NSURLConnectionconnectionWithRequest:requestdelegate:self];

这里用到了代理,那肯定要遵守协议了.遵守NSURLConnectionDataDelegate协议.
进去看看有几个代理方法,其实我们能用到的也就三个。

/**
*请求失败时调用(请求超时、网络异常)
*
*@paramerror错误原因
*/
-(void)connection:(NSURLConnection*)connectiondidFailWithError:(NSError*)error
{

}
/**
*1.接收到服务器的响应就会调用
*
*@paramresponse响应
*/
-(void)connection:(NSURLConnection*)connectiondidReceiveResponse:(NSURLResponse*)response
{

}
/**
*2.当接收到服务器返回的实体数据时调用(具体内容,这个方法可能会被调用多次)
*
*@paramdata这次返回的数据
*/
-(void)connection:(NSURLConnection*)connectiondidReceiveData:(NSData*)data
{

}
/**
*3.加载完毕后调用(服务器的数据已经完全返回后)
*/
-(void)connectionDidFinishLoading:(NSURLConnection*)connection
{
}


通过执行下载操作,分别log上面三个方法,会发现didReceiveData这个方法会被频繁的调用,每次都会传回来一部分data,下面是官方api对这个方法的说明[/code]
iscalledwithasingleimmutableNSDataobjecttothedelegate,
representingthenextportionofthedataloadedfromtheconnection.Thisistheonlyguaranteedforthedelegatetoreceivethedatafromtheresourceload.


[/code]
由此我们可以知道,这种下载方式是通过这个代理方法每次传回来一部分文件,最终我们把每次传回来的数据合并成一个我们需要的文件。

这时候我们通常想到的方法是定义一个全局的NSMutableData,接受到响应的时候初始化这个MutableData,在didReceiveData方法里面去拼接
[self.totalDataappendData:data];
最后在完成下载的方法里面吧整个MutableData写入沙盒。
代码如下:

@property(weak,nonatomic)IBOutletUIProgressView*myPregress;

@property(nonatomic,strong)NSMutableData*fileData;

/**
*文件的总长度
*/
@property(nonatomic,assign)longlongtotalLength;

/**
*1.接收到服务器的响应就会调用
*
*@paramresponse响应
*/
-(void)connection:(NSURLConnection*)connectiondidReceiveResponse:(NSURLResponse*)response
{
self.fileData=[NSMutableDatadata];
//获取要下载的文件的大小
self.totalLength=response.expectedContentLength;
}
/**
*2.当接收到服务器返回的实体数据时调用(具体内容,这个方法可能会被调用多次)
*
*@paramdata这次返回的数据
*/
-(void)connection:(NSURLConnection*)connectiondidReceiveData:(NSData*)data
{
[self.fileDataappendData:data];
self.myPregress.progress=(double)self.fileData.length/self.totalLength;
}
/**
*3.加载完毕后调用(服务器的数据已经完全返回后)
*/
-(void)connectionDidFinishLoading:(NSURLConnection*)connection
{
//拼接文件路径
NSString*cache=[NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES)lastObject];
NSString*file=[cachestringByAppendingPathComponent:response.suggestedFilename];

//写到沙盒中
[self.fileDatawriteToFile:fileatomically:YES];
}

我这里下载的是javajdk。(度娘的地址)
注意:通常大文件下载是需要给用户展示下载进度的。
这个数值是已经下载的数据大小/要下载的文件总大小
已经下载的数据我们可以记录,要下载的文件总大小在服务器返回的响应头里面可以拿到,在接受到响应的方法里执行[/code]
NSHTTPURLResponse*res=(NSHTTPURLResponse*)response;

NSDictionary*headerDic=res.allHeaderFields;
NSLog(@"%@",headerDic);
self.fileLength=[[headerDicobjectForKey:@"Content-Length"]intValue];


[/code]
不得不说苹果太为开发者考虑了,我们不必这么麻烦的去获取文件总大小了,
response.expectedContentLength这句代码就搞定了。
response.suggestedFilename这句代表获取下载的文件名

这样我们确实可以下载文件,最后拿到的文件也能正常运行

但是有个致命的问题,内存!用来接受文件的NSMutableData一直都在内存中,会随着文件的下载一直变大,

所有这种处理方式绝对是不合理的。

合理的方式在我们获取一部分data的时候就写入沙盒中,然后释放内存中的data。

这里要用到NSFilehandle这个类,这个类可以实现对文件的读取、写入、更新。
下面总结了一些常用的NSFileHandle的方法,在这个表中,fh是一个NSFileHandle对象,data是一个NSData对象,path是一个NSString对象,offset是易额Unsignedlonglong变量。

具体关于NSFileHandle的用法各位自行搜索。

在接受到响应的时候就在沙盒中创建一个空的文件,然后每次接收到数据的时候就拼接到这个文件的最后面,通过-(unsignedlonglong)seekToEndOfFile;这个方法

-(void)connection:(NSURLConnection*)connectiondidReceiveResponse:(NSURLResponse*)response
{
//文件路径
NSString*ceches=[NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES)lastObject];
NSString*filepath=[cechesstringByAppendingPathComponent:response.suggestedFilename];

//创建一个空的文件到沙盒中
NSFileManager*mgr=[NSFileManagerdefaultManager];
[mgrcreateFileAtPath:filepathcontents:nilattributes:nil];

//创建一个用来写数据的文件句柄对象
self.writeHandle=[NSFileHandlefileHandleForWritingAtPath:filepath];

//获得文件的总大小
self.totalLength=response.expectedContentLength;

}
/**
*2.当接收到服务器返回的实体数据时调用(具体内容,这个方法可能会被调用多次)
*
*@paramdata这次返回的数据
*/
-(void)connection:(NSURLConnection*)connectiondidReceiveData:(NSData*)data
{
//移动到文件的最后面
[self.writeHandleseekToEndOfFile];

//将数据写入沙盒
[self.writeHandlewriteData:data];

//累计写入文件的长度
self.currentLength+=data.length;

//下载进度
self.myPregress.progress=(double)self.currentLength/self.totalLength;
}
/**
*3.加载完毕后调用(服务器的数据已经完全返回后)
*/
-(void)connectionDidFinishLoading:(NSURLConnection*)connection
{
self.currentLength=0;
self.totalLength=0;

//关闭文件
[self.writeHandlecloseFile];
self.writeHandle=nil;
}


这样在下载过程中内存就会一直很稳定了,并且下载的文件也是没问题的。

[/code]
原文:http://www.90159.com/2016/01/07/ios-big-file-download/


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: