小文件下载
NSURLConnection-delegate实现,可以监听到下载的进度,但是还是无法解决内存飙升的问题,所以仅限小文件下载
1 @interface ViewController ()<NSURLConnectionDataDelegate>
2 @property (weak, nonatomic) IBOutlet UIProgressView *progressView;
3 /** 接收数据的data */
4 @property (nonatomic, strong) NSMutableData *fileData; 6 /*文件名称 */
7 @property (nonatomic, strong) NSString *fileName;
8 @property (nonatomic, assign) NSInteger totalSize;
9 @end
10 @implementation ViewController
11 -(NSMutableData *)fileData
12 {
13 if (_fileData == nil) {
14 _fileData = [NSMutableData data];
15 }
16 return _fileData;
17 }
18 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
19 {
20 //1.确定URL
21 NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
22 //2.创建请求对象
23 NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url];
24 //3.设置代理,发送请求
25 [NSURLConnection connectionWithRequest:request delegate:self];
26 }
27 #pragma mark - NSURLConnectionDataDelegate
28 -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
29 {
30 //suggestedFilename:服务器端推荐的名称,其实就是url的最后一个节点
31 self.fileName = response.suggestedFilename;
32 //expectedContentLength:获得文件总大小
33
34 self.totalSize = response.expectedContentLength;
35 }
36 -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
37 {
38 //拼接数据
39 [self.fileData appendData:data];
40 //计算文件的下载进度== 已经下载的大小/文件的总大小
41 NSLog(@"%f",1.0 * self.fileData.length /self.totalSize);
42 //进度UI43 self.progressView.progress = 1.0 * self.fileData.length /self.totalSize;
44 }
45 -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
46 {
47 }
48 -(void)connectionDidFinishLoading:(NSURLConnection *)connection
49 {
50 //写数据到磁盘
51 NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
52 //拼接全路径
53 NSString *fullPath = [caches stringByAppendingPathComponent:self.fileName];
54 //写沙盒
55 [self.fileData writeToFile:fullPath atomically:YES];
56 }
57 @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接收到服务器的响应的方法中代码改动:
//注意点:response
.expectedContentLength获得是本次请求要下载的文件的大小(并非是完整的文件的大小)
//文件的总大小 == 本次要下载的文件大小 + 已经下载的文件的大小
self.totalLength = response.expectedContentLength + self.currentLength;
// 判断当前是否已经下载过,如果当前文件已经存在,那么直接返回,不然新下载的大小会覆盖原来文件,导致前面文件丢失
if (self.currentLength >0) {
return;
}
大文件输出流下载
使用输出流也可以实现和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;