NSURLConnection 文件下载的BUG及解决思路、方案
来源:互联网 发布:电脑锣编程代码 编辑:程序博客网 时间:2024/05/17 04:18
NSURLConnection 文件下载的BUG及解决思路、方案
// 一般在文件名下载的过程中,应该告诉用户下载进度(进度条).
思路: NSUrlConnection : 下载.
{
小文件:直接利用 block 回调(异步请求,下载好的文件就是block 回调中的 data).
中文件:会造成内存暴涨{先将文件下载到内存中(data),然后再写入磁盘}.为了防止内存暴涨,不能直接使用block回调.
大文件:会造成内存暴涨{先将文件下载到内存中(data),然后再写入磁盘}.为了防止内存暴涨,不能直接使用block回调.
}
}
NSUrlConnection 下载 Bug 汇总:
1.异步回调下载
{
如果下载大文件:内存暴涨. (下载下来的内容存储在data中,整个内容下载完毕之后,都存储在内存中,然后再一次性写入沙盒.)
}
2.NSUrlConnectionDownloadDelegate
{
1. 可以监听下载进度.
2. 找不到下载好的文件.
}
3.NSUrlConnectionDataDelegate
{
1. 需要自己写业务逻辑监听下载进度.
2. 将每次下载好的文件内容用一个属性保存起来,然后等文件都下载完毕之后再写入沙盒
{
1>内存暴涨,原因同异步回调下载.
2>如果不及时清除属性中保存的内容,内存占用量会一直很大.
}
}
4.同时使用NSUrlConnectionDownloadDelegate 和 NSUrlConnectionDataDelegate,持续下载数据的方法只会调用NSUrlConnectionDownloadDelegate.所以不能同时使用这两个代理.
5.NSUrlConnectionDataDelegate
{
1. 下载进度不能平滑的显示(一段一段的显示):默认下载回调是在主线程进行的,下载过程阻塞了主线程,UI不能够及时的显示.
2. 边下载,边写入沙盒
{
1. NSFileHandle
2. NSOutputStream
如果下载多次,都会造成本地下载好的文件变大...
}
}
6.NSUrlConnectionDataDelegate
{
1. 设置 代理回调在子线程: 代理方法的调用确实在子线程,并且是多条线程.但是,主线程在执行UI 操作的时候,后台线程会卡住(会阻塞下载).
2. 开启新的下载任务之前,要检查本地文件和服务器文件的大小.做业务逻辑的判断.
}
7.NSUrlConnectionDataDelegate
{
1. 将网络连接放在子线程,这样和主线程就没有任何关系.但是需要开启子线程运行循环之后,才能够执行下载的代理方法. : NSUrlConnectionDataDelegate是一个特殊的运行循环.(下载完毕之后,运行循环自动停止.) --> 保证在执行UI 操作的时候,后台可以继续进行下载.设置代理回调为非主队列,可以在多条线程同时下载.
2. 下载业务逻辑
{
1. 本地文件大小 > 服务器文件大小 : 1> 删除本地文件 2>重新开始下载
2. 本地文件大小 < 服务器文件大小 :
{
*1如果本地文件大小 = 0 : 直接从0开始下载(重新开始下载).
*2如果本地文件大小 > 0 : 断点续传:
{
设置 Range 属性,告诉服务器断点续传开始的位置.
Range 格式: [bytes=%ld-%ld,X,Y] ,从X位置开始,下载Y个字节.
设置 Range 属性之后,服务器返回的状态码会变成206 .
}
}
}
}
思路: NSUrlConnection : 下载.
{
小文件:直接利用 block 回调(异步请求,下载好的文件就是block 回调中的 data).
中文件:会造成内存暴涨{先将文件下载到内存中(data),然后再写入磁盘}.为了防止内存暴涨,不能直接使用block回调.
大文件:会造成内存暴涨{先将文件下载到内存中(data),然后再写入磁盘}.为了防止内存暴涨,不能直接使用block回调.
}
}
NSUrlConnection 下载 Bug 汇总:
1.异步回调下载
{
如果下载大文件:内存暴涨. (下载下来的内容存储在data中,整个内容下载完毕之后,都存储在内存中,然后再一次性写入沙盒.)
}
2.NSUrlConnectionDownloadDelegate
{
1. 可以监听下载进度.
2. 找不到下载好的文件.
}
3.NSUrlConnectionDataDelegate
{
1. 需要自己写业务逻辑监听下载进度.
2. 将每次下载好的文件内容用一个属性保存起来,然后等文件都下载完毕之后再写入沙盒
{
1>内存暴涨,原因同异步回调下载.
2>如果不及时清除属性中保存的内容,内存占用量会一直很大.
}
}
4.同时使用NSUrlConnectionDownloadDelegate 和 NSUrlConnectionDataDelegate,持续下载数据的方法只会调用NSUrlConnectionDownloadDelegate.所以不能同时使用这两个代理.
5.NSUrlConnectionDataDelegate
{
1. 下载进度不能平滑的显示(一段一段的显示):默认下载回调是在主线程进行的,下载过程阻塞了主线程,UI不能够及时的显示.
2. 边下载,边写入沙盒
{
1. NSFileHandle
2. NSOutputStream
如果下载多次,都会造成本地下载好的文件变大...
}
}
6.NSUrlConnectionDataDelegate
{
1. 设置 代理回调在子线程: 代理方法的调用确实在子线程,并且是多条线程.但是,主线程在执行UI 操作的时候,后台线程会卡住(会阻塞下载).
2. 开启新的下载任务之前,要检查本地文件和服务器文件的大小.做业务逻辑的判断.
}
7.NSUrlConnectionDataDelegate
{
1. 将网络连接放在子线程,这样和主线程就没有任何关系.但是需要开启子线程运行循环之后,才能够执行下载的代理方法. : NSUrlConnectionDataDelegate是一个特殊的运行循环.(下载完毕之后,运行循环自动停止.) --> 保证在执行UI 操作的时候,后台可以继续进行下载.设置代理回调为非主队列,可以在多条线程同时下载.
2. 下载业务逻辑
{
1. 本地文件大小 > 服务器文件大小 : 1> 删除本地文件 2>重新开始下载
2. 本地文件大小 < 服务器文件大小 :
{
*1如果本地文件大小 = 0 : 直接从0开始下载(重新开始下载).
*2如果本地文件大小 > 0 : 断点续传:
{
设置 Range 属性,告诉服务器断点续传开始的位置.
Range 格式: [bytes=%ld-%ld,X,Y] ,从X位置开始,下载Y个字节.
设置 Range 属性之后,服务器返回的状态码会变成206 .
}
}
}
}
具体的方案:
所有的网络请求只能是异步请求,。。。。同步请求(不会创建子线程)会阻塞主线程。。。。
小文件:直接利用block回调,把(data 保存到本地的路径就好了)下载好的文件就是block回调中的data 。小文件没问题。
NSString * string = @"http://192.168.1.254/xiaowenjian2.zip";
NSURL * url = [NSURLURLWithString:string];
NSURLRequest * request =[ NSURLRequest requestWithURL:url];
//发送异步请求,下载文件
[NSURLConnectionsendAsynchronousRequest:requestqueue:[NSOperationQueuemainQueue]completionHandler:^(NSURLResponse* _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
[data writeToFile:@"/Users/zzx/Desktop/xiaowenjian2.zip"atomically:YES];
}];
中文件、大文件:BUG 1.会造成内存暴涨(先将文件下载到内存中,然后再写入磁盘) ,为了防止内存暴涨,不能直接使用block回调.——>解决方法,用NSURLConnectionDownloadDelegate。
- (void)touchesBegan:(NSSet<UITouch*> *)touches withEvent:(UIEvent*)event
{
// NSURLConnectionDownloadDelegate
// 可以监听下载进度.
// 但是,文件下载完毕之后,找不到下载好的文件在哪里.(destinationURL的位置没有文件)
NSLog(@"touchesBegan");
// 文件下载:
// http://127.0.0.1/xiaowenjian1.jpg :网络接口地址(文件下载地址)
// http://127.0.0.1/xiaowenjian2.zip
// http://127.0.0.1/zhongwenjian.zip
NSString *urlString = @"http://127.0.0.1/dawenjian.zip";
// 1. 创建请求
NSURL *url = [NSURLURLWithString:urlString];
NSURLRequest *request = [NSURLRequestrequestWithURL:url];
// 下载,代理.
// 创建网络连接,设置代理对象
NSURLConnection *conn = [[NSURLConnectionalloc]initWithRequest:requestdelegate:self];
// 代理对象创建完毕之后,就会自动调用代理方法.不需要手动启动的.
[conn start];
}
#pragma NSURLConnectionDownloadDelegate
//频繁地从服务器下载资源(文件).知道文件下载完毕.(取消下载图片的操作,这一个方法是判断取消的具体位置.)
// bytesWritten:本次调用方法下载的数据量.
// totalBytesWritten:已经下载的总数据量.
// expectedTotalBytes: :需要下载的数据总量(下载资源的大小.)
- (void)connection:(NSURLConnection*)connection didWriteData:(longlong)bytesWritten totalBytesWritten:(longlong)totalBytesWritten expectedTotalBytes:(longlong) expectedTotalBytes
{
// 下载单位:字节
NSLog(@"本次下载的大小:%lld已经下载了:%lld总共需要下载:%lld %@",bytesWritten,totalBytesWritten,expectedTotalBytes, [NSThreadcurrentThread]);
}
- (void)connectionDidResumeDownloading:(NSURLConnection*)connection totalBytesWritten:(longlong)totalBytesWritten expectedTotalBytes:(longlong) expectedTotalBytes
{
NSLog(@"断点续传的方法,不使用.%@",[NSThreadcurrentThread]);
}
- (void)connectionDidFinishDownloading:(NSURLConnection*)connection destinationURL:(NSURL*) destinationURL
{
// destinationURL:文件下载完毕之后,保存的地址.
NSLog(@"文件下载完毕,%@ %@",destinationURL,[NSThreadcurrentThread]);
}
{
// NSURLConnectionDownloadDelegate
// 可以监听下载进度.
// 但是,文件下载完毕之后,找不到下载好的文件在哪里.(destinationURL的位置没有文件)
NSLog(@"touchesBegan");
// 文件下载:
// http://127.0.0.1/xiaowenjian1.jpg :网络接口地址(文件下载地址)
// http://127.0.0.1/xiaowenjian2.zip
// http://127.0.0.1/zhongwenjian.zip
NSString *urlString = @"http://127.0.0.1/dawenjian.zip";
// 1. 创建请求
NSURL *url = [NSURLURLWithString:urlString];
NSURLRequest *request = [NSURLRequestrequestWithURL:url];
// 下载,代理.
// 创建网络连接,设置代理对象
NSURLConnection *conn = [[NSURLConnectionalloc]initWithRequest:requestdelegate:self];
// 代理对象创建完毕之后,就会自动调用代理方法.不需要手动启动的.
[conn start];
}
#pragma NSURLConnectionDownloadDelegate
//频繁地从服务器下载资源(文件).知道文件下载完毕.(取消下载图片的操作,这一个方法是判断取消的具体位置.)
// bytesWritten:本次调用方法下载的数据量.
// totalBytesWritten:已经下载的总数据量.
// expectedTotalBytes: :需要下载的数据总量(下载资源的大小.)
- (void)connection:(NSURLConnection*)connection didWriteData:(longlong)bytesWritten totalBytesWritten:(longlong)totalBytesWritten expectedTotalBytes:(longlong) expectedTotalBytes
{
// 下载单位:字节
NSLog(@"本次下载的大小:%lld已经下载了:%lld总共需要下载:%lld %@",bytesWritten,totalBytesWritten,expectedTotalBytes, [NSThreadcurrentThread]);
}
- (void)connectionDidResumeDownloading:(NSURLConnection*)connection totalBytesWritten:(longlong)totalBytesWritten expectedTotalBytes:(longlong) expectedTotalBytes
{
NSLog(@"断点续传的方法,不使用.%@",[NSThreadcurrentThread]);
}
- (void)connectionDidFinishDownloading:(NSURLConnection*)connection destinationURL:(NSURL*) destinationURL
{
// destinationURL:文件下载完毕之后,保存的地址.
NSLog(@"文件下载完毕,%@ %@",destinationURL,[NSThreadcurrentThread]);
}
2.NSURLConnectionDownloadDelegate可以监听,但找不到tmp里面下载好的文件(解决方法:换代理方法用NSURLConnectionDataDelegate)
代理对象创建完毕后,代理方法会自动调用自动下载,不需要手动下载数据。
实体内容就是我们想要的数据,
响应头最先调用返回的是数据类型和大小,服务器信息
步骤:1.实例化一个NSMutableData,以保存下载好的数据
2。在 接收到数据(实体内容)拼接数据
3.在数据下载完毕后写入本地磁盘,用MD5校验两个文件时否一致,用终端,
#import"ViewController.h"
@interfaceViewController ()<NSURLConnectionDataDelegate,NSURLConnectionDownloadDelegate>
@property(nonatomic,strong)NSMutableData*fileData;
@end
@implementationViewController
-(NSMutableData*)fileData
{
if (!_fileData) {
_fileData = [NSMutableDatadata];
}
return _fileData;
}
- (void)viewDidLoad {
[superviewDidLoad];
// NSURLConnectionDataDelegate
// 直接用一个可变二进制数据文件接收下载好的数据,数据接收完毕之后,在本地保存:内存依然暴涨.文件写入本地磁盘之后,如果不及时释放下载好的文件,会造成内存一个很大.
// NSURLConnectionDataDelegate :检测下载进度,需要自己写业务逻辑.
// NSURLConnectionDownloadDelegate 处理下载进度, NSURLConnectionDataDelegate处理下载数据.
// NSURLConnectionDownloadDelegate 和 NSURLConnectionDataDelegate 方法(持续下载数据的方法)不可以同时调用.
}
- (void)touchesBegan:(NSSet<UITouch*> *)touches withEvent:(UIEvent*)event
{
NSLog(@"touchesBegan");
// 文件下载:
// http://127.0.0.1/xiaowenjian1.jpg :网络接口地址(文件下载地址)
// http://127.0.0.1/xiaowenjian2.zip
// http://127.0.0.1/zhongwenjian.zip
NSString *urlString = @"http://127.0.0.1/dawenjian.zip";
// 1. 创建请求
NSURL *url = [NSURLURLWithString:urlString];
NSURLRequest *request = [NSURLRequestrequestWithURL:url];
// 下载,代理.
// 创建网络连接,设置代理对象
NSURLConnection *conn = [[NSURLConnectionalloc]initWithRequest:requestdelegate:self];
// 代理对象创建完毕之后,就会自动调用代理方法.不需要手动启动的.
[conn start];
}
#pragma NSURLConnectionDataDelegate
//接收到 响应头信息的时候就会调用.(最先调用的方法.),只会调用一次.
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
NSLog(@"%@ %@",response, [NSThreadcurrentThread]);
}
//接收到 数据(实体内容)的时候就会调用.也会调用多次.
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
NSLog(@"本次接收到%ld 的数据 %@",data.length,[NSThreadcurrentThread]);
// 拼接下载好的数据
[self.fileDataappendData:data];
}
//网络完成之后(数据下载完毕),就会调用.
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSLog(@"下载完毕...%@",[NSThreadcurrentThread]);
// 数据拼接完毕,保存在本地.
[self.fileDatawriteToFile:@"/Users/teacher/Desktop/111.zip"atomically:YES];
self.fileData= nil;
}
#pragma NSURLConnectionDownloadDelegate
- (void)connection:(NSURLConnection*)connection didWriteData:(longlong)bytesWritten totalBytesWritten:(longlong)totalBytesWritten expectedTotalBytes:(longlong) expectedTotalBytes
{
NSLog(@"-------------------1111");
}
- (void)connectionDidFinishDownloading:(NSURLConnection*)connection destinationURL:(NSURL*) destinationURL
{
NSLog(@"-------------%@",destinationURL);
}
@interfaceViewController ()<NSURLConnectionDataDelegate,NSURLConnectionDownloadDelegate>
@property(nonatomic,strong)NSMutableData*fileData;
@end
@implementationViewController
-(NSMutableData*)fileData
{
if (!_fileData) {
_fileData = [NSMutableDatadata];
}
return _fileData;
}
- (void)viewDidLoad {
[superviewDidLoad];
// NSURLConnectionDataDelegate
// 直接用一个可变二进制数据文件接收下载好的数据,数据接收完毕之后,在本地保存:内存依然暴涨.文件写入本地磁盘之后,如果不及时释放下载好的文件,会造成内存一个很大.
// NSURLConnectionDataDelegate :检测下载进度,需要自己写业务逻辑.
// NSURLConnectionDownloadDelegate 处理下载进度, NSURLConnectionDataDelegate处理下载数据.
// NSURLConnectionDownloadDelegate 和 NSURLConnectionDataDelegate 方法(持续下载数据的方法)不可以同时调用.
}
- (void)touchesBegan:(NSSet<UITouch*> *)touches withEvent:(UIEvent*)event
{
NSLog(@"touchesBegan");
// 文件下载:
// http://127.0.0.1/xiaowenjian1.jpg :网络接口地址(文件下载地址)
// http://127.0.0.1/xiaowenjian2.zip
// http://127.0.0.1/zhongwenjian.zip
NSString *urlString = @"http://127.0.0.1/dawenjian.zip";
// 1. 创建请求
NSURL *url = [NSURLURLWithString:urlString];
NSURLRequest *request = [NSURLRequestrequestWithURL:url];
// 下载,代理.
// 创建网络连接,设置代理对象
NSURLConnection *conn = [[NSURLConnectionalloc]initWithRequest:requestdelegate:self];
// 代理对象创建完毕之后,就会自动调用代理方法.不需要手动启动的.
[conn start];
}
#pragma NSURLConnectionDataDelegate
//接收到 响应头信息的时候就会调用.(最先调用的方法.),只会调用一次.
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
NSLog(@"%@ %@",response, [NSThreadcurrentThread]);
}
//接收到 数据(实体内容)的时候就会调用.也会调用多次.
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
NSLog(@"本次接收到%ld 的数据 %@",data.length,[NSThreadcurrentThread]);
// 拼接下载好的数据
[self.fileDataappendData:data];
}
//网络完成之后(数据下载完毕),就会调用.
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSLog(@"下载完毕...%@",[NSThreadcurrentThread]);
// 数据拼接完毕,保存在本地.
[self.fileDatawriteToFile:@"/Users/teacher/Desktop/111.zip"atomically:YES];
self.fileData= nil;
}
#pragma NSURLConnectionDownloadDelegate
- (void)connection:(NSURLConnection*)connection didWriteData:(longlong)bytesWritten totalBytesWritten:(longlong)totalBytesWritten expectedTotalBytes:(longlong) expectedTotalBytes
{
NSLog(@"-------------------1111");
}
- (void)connectionDidFinishDownloading:(NSURLConnection*)connection destinationURL:(NSURL*) destinationURL
{
NSLog(@"-------------%@",destinationURL);
}
@end
!!!!内存还是暴涨!监听下载进度条! 需要自己写业务逻辑
内存还是暴涨的解决方法:数据追加( 边下载边保存(写入本地).还要保证后续下载的数据追加在之前下载数据的后面.)
第一种方法:数据追加:
1.NSFileHandle:文件操作句柄,用来操作文件内部——>造成的BUG,如果多次下载,会造成下载完毕后的文件变大,需要做业务逻辑
2.NSFileManager:用来操作文件(获取文件信息/删除/移动/复制。。。)
#import"ViewController.h"
@interfaceViewController ()<NSURLConnectionDataDelegate>
//文件下载完毕之后,保存的路径.
@property(nonatomic,copy)NSString*filePath;
@end
@implementationViewController
- (void)viewDidLoad {
[superviewDidLoad];
// 1. 下载过程中,内存不能变大.
// 边下载边保存(写入本地).还要保证后续下载的数据追加在之前下载数据的后面.
// 数据追加:
// 1. NSFileHandle: 文件操作句柄,用来操作文件内部
// NSFileManager:用来操纵文件(获得文件信息/删除/移动/复制...)
// 如果多次下载,会造成下载完毕的文件变大...(2/3/4/5倍增加.),需要做业务逻辑处理.
// 创建文件句柄
// 根据文件路径,实例化文件操作句柄(写入)
// 如果传入的路径不存在,文件句柄会实例化失败. nil.
// 如果传入的文件路径存在,文件句柄会实例化成功,并且指向这个需要操作的文件.
NSFileHandle *handle = [NSFileHandlefileHandleForWritingAtPath:@""];
// 2.监听下载进度.
}
- (void)touchesBegan:(NSSet<UITouch*> *)touches withEvent:(UIEvent*)event
{
NSLog(@"touchesBegan");
NSString *urlString = @"http://127.0.0.1/dawenjian.zip";
// 1. 创建请求
NSURL *url = [NSURLURLWithString:urlString];
NSURLRequest *request = [NSURLRequestrequestWithURL:url];
// 下载,代理.
// 创建网络连接,设置代理对象
NSURLConnection *conn = [[NSURLConnectionalloc]initWithRequest:requestdelegate:self];
// 代理对象创建完毕之后,就会自动调用代理方法.不需要手动启动的.
[conn start];
}
#pragma NSURLConnectionDataDelegate
//接收到 响应头信息的时候就会调用.(最先调用的方法.),只会调用一次.
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
NSLog(@"%@ %@",response, [NSThreadcurrentThread]);
// 准备下载文件数据之前,实例化文件下载路径.
self.filePath= [NSStringstringWithFormat:@"/Users/teacher/Desktop/%@",response.suggestedFilename];
}
//接收到 数据(实体内容)的时候就会调用.也会调用多次.
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
NSLog(@"本次接收到%ld 的数据 %@",data.length,[NSThreadcurrentThread]);
// 实例化文件句柄,操纵文件
NSFileHandle *handle = [NSFileHandlefileHandleForWritingAtPath:self.filePath];
if (handle) {
// 往文件后面追加文件内容.
// 1. 将文件句柄移动到文件最后(末尾)
[handle seekToEndOfFile];
// 2. 追加文件
[handle writeData:data];
[handle closeFile];
}else
{
// 第一次实例化路径(创建文件)
// 如果这个路径下有文件了,会自动覆盖;如果没有文件,会创建一个文件.
[data writeToFile:self.filePathatomically:YES];
}
@interfaceViewController ()<NSURLConnectionDataDelegate>
//文件下载完毕之后,保存的路径.
@property(nonatomic,copy)NSString*filePath;
@end
@implementationViewController
- (void)viewDidLoad {
[superviewDidLoad];
// 1. 下载过程中,内存不能变大.
// 边下载边保存(写入本地).还要保证后续下载的数据追加在之前下载数据的后面.
// 数据追加:
// 1. NSFileHandle: 文件操作句柄,用来操作文件内部
// NSFileManager:用来操纵文件(获得文件信息/删除/移动/复制...)
// 如果多次下载,会造成下载完毕的文件变大...(2/3/4/5倍增加.),需要做业务逻辑处理.
// 创建文件句柄
// 根据文件路径,实例化文件操作句柄(写入)
// 如果传入的路径不存在,文件句柄会实例化失败. nil.
// 如果传入的文件路径存在,文件句柄会实例化成功,并且指向这个需要操作的文件.
NSFileHandle *handle = [NSFileHandlefileHandleForWritingAtPath:@""];
// 2.监听下载进度.
}
- (void)touchesBegan:(NSSet<UITouch*> *)touches withEvent:(UIEvent*)event
{
NSLog(@"touchesBegan");
NSString *urlString = @"http://127.0.0.1/dawenjian.zip";
// 1. 创建请求
NSURL *url = [NSURLURLWithString:urlString];
NSURLRequest *request = [NSURLRequestrequestWithURL:url];
// 下载,代理.
// 创建网络连接,设置代理对象
NSURLConnection *conn = [[NSURLConnectionalloc]initWithRequest:requestdelegate:self];
// 代理对象创建完毕之后,就会自动调用代理方法.不需要手动启动的.
[conn start];
}
#pragma NSURLConnectionDataDelegate
//接收到 响应头信息的时候就会调用.(最先调用的方法.),只会调用一次.
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
NSLog(@"%@ %@",response, [NSThreadcurrentThread]);
// 准备下载文件数据之前,实例化文件下载路径.
self.filePath= [NSStringstringWithFormat:@"/Users/teacher/Desktop/%@",response.suggestedFilename];
}
//接收到 数据(实体内容)的时候就会调用.也会调用多次.
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
NSLog(@"本次接收到%ld 的数据 %@",data.length,[NSThreadcurrentThread]);
// 实例化文件句柄,操纵文件
NSFileHandle *handle = [NSFileHandlefileHandleForWritingAtPath:self.filePath];
if (handle) {
// 往文件后面追加文件内容.
// 1. 将文件句柄移动到文件最后(末尾)
[handle seekToEndOfFile];
// 2. 追加文件
[handle writeData:data];
[handle closeFile];
}else
{
// 第一次实例化路径(创建文件)
// 如果这个路径下有文件了,会自动覆盖;如果没有文件,会创建一个文件.
[data writeToFile:self.filePathatomically:YES];
}
}
//网络完成之后(数据下载完毕),就会调用.
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSLog(@"下载完毕...%@",[NSThreadcurrentThread]);
}
//网络完成之后(数据下载完毕),就会调用.
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSLog(@"下载完毕...%@",[NSThreadcurrentThread]);
}
@end
第二种方法:文件的数据流/输入输出流:NSOutputStream—>负责建立一个“管道”,让数据流顺着这个“管道”流入指定的文件——>造成的BUG,如果多次下载,会造成下载完毕后的文件变大,需要做业务逻辑。
//实例化对象
// 如果这个文件路径不存在,会自动创建一个空文件.如果文件存在,就直接在文件后面追加文件. // NSOutputStream *stream = [[NSOutputStream alloc] initToFileAtPath:self.filePath append:YES];
NSOutputStream稳定性不如NSFileHandle
步骤:注意,管道式需要手动开启的 open
#import"ViewController.h"
@interfaceViewController ()<NSURLConnectionDataDelegate>
//文件下载完毕之后,保存的路径.
@property(nonatomic,copy)NSString*filePath;
//文件输入输出流管道.
@property(nonatomic,strong)NSOutputStream*stream;
@end
@implementationViewController
- (void)touchesBegan:(NSSet<UITouch*> *)touches withEvent:(UIEvent*)event
{
NSLog(@"touchesBegan");
// http://dldir1.qq.com/qqfile/QQforMac/QQ_V4.0.4.dmg
NSString *urlString = @"http://dldir1.qq.com/qqfile/QQforMac/QQ_V4.0.4.dmg";
// 1. 创建请求
NSURL *url = [NSURLURLWithString:urlString];
NSURLRequest *request = [NSURLRequestrequestWithURL:url];
// 下载,代理.
// 创建网络连接,设置代理对象
NSURLConnection *conn = [[NSURLConnectionalloc]initWithRequest:requestdelegate:self];
// 代理对象创建完毕之后,就会自动调用代理方法.不需要手动启动的.
[conn start];
}
#pragma NSURLConnectionDataDelegate
//接收到 响应头信息的时候就会调用.(最先调用的方法.),只会调用一次.
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
NSLog(@"%@ %@",response, [NSThreadcurrentThread]);
// 准备下载文件数据之前,实例化文件下载路径.
self.filePath= [NSStringstringWithFormat:@"/Users/teacher/Desktop/%@",response.suggestedFilename];
// 创建管道.
self.stream= [[NSOutputStreamalloc]initToFileAtPath:self.filePathappend:YES];
// 管道是需要手动开启的.
[self.streamopen];
}
//接收到 数据(实体内容)的时候就会调用.也会调用多次.
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
NSLog(@"本次接收到%ld 的数据 %@",data.length,[NSThreadcurrentThread]);
// 顺着管道,流入数据.
[self.streamwrite:[databytes]maxLength:data.length];
// NSLog(@"%@",data);
}
//网络完成之后(数据下载完毕),就会调用.
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSLog(@"下载完毕...%@",[NSThreadcurrentThread]);
// 关闭管道.
[self.streamclose];
{
NSLog(@"touchesBegan");
// http://dldir1.qq.com/qqfile/QQforMac/QQ_V4.0.4.dmg
NSString *urlString = @"http://dldir1.qq.com/qqfile/QQforMac/QQ_V4.0.4.dmg";
// 1. 创建请求
NSURL *url = [NSURLURLWithString:urlString];
NSURLRequest *request = [NSURLRequestrequestWithURL:url];
// 下载,代理.
// 创建网络连接,设置代理对象
NSURLConnection *conn = [[NSURLConnectionalloc]initWithRequest:requestdelegate:self];
// 代理对象创建完毕之后,就会自动调用代理方法.不需要手动启动的.
[conn start];
}
#pragma NSURLConnectionDataDelegate
//接收到 响应头信息的时候就会调用.(最先调用的方法.),只会调用一次.
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
NSLog(@"%@ %@",response, [NSThreadcurrentThread]);
// 准备下载文件数据之前,实例化文件下载路径.
self.filePath= [NSStringstringWithFormat:@"/Users/teacher/Desktop/%@",response.suggestedFilename];
// 创建管道.
self.stream= [[NSOutputStreamalloc]initToFileAtPath:self.filePathappend:YES];
// 管道是需要手动开启的.
[self.streamopen];
}
//接收到 数据(实体内容)的时候就会调用.也会调用多次.
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
NSLog(@"本次接收到%ld 的数据 %@",data.length,[NSThreadcurrentThread]);
// 顺着管道,流入数据.
[self.streamwrite:[databytes]maxLength:data.length];
// NSLog(@"%@",data);
}
//网络完成之后(数据下载完毕),就会调用.
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSLog(@"下载完毕...%@",[NSThreadcurrentThread]);
// 关闭管道.
[self.streamclose];
}
(显示问题 )监听下载进度条的解决方法:
1.需要我那件总大小(定义一个属性 =respond。expectedContentLenght),当前下载 的数据量(定义一个属性 = data。length)——>BUG:直接设置进度条,进度条不能平滑是显示下载进度
#import"ViewController.h"
@interfaceViewController ()<NSURLConnectionDataDelegate>
//文件下载完毕之后,保存的路径.
@property(nonatomic,copy)NSString*filePath;
//文件输入输出流管道.
@property(nonatomic,strong)NSOutputStream*stream;
//文件总大小
@property(nonatomic,assign)long long expectedLength;
//当前已经下载的数据量
@property(nonatomic,assign)long long totalBytes;
//下载进度条
@property(nonatomic,strong)UIProgressView *progressView;
@end
@implementationViewController
-(UIProgressView*)progressView
{
if (!_progressView) {
_progressView = [[UIProgressViewalloc]initWithFrame:CGRectMake(20,50,335,2)];
// 进度条颜色
_progressView.tintColor= [UIColorgreenColor];
[self.viewaddSubview:_progressView];
}
return _progressView;
}
- (void)viewDidLoad {
[superviewDidLoad];
// 1. 下载过程中,内存不能变大.
// 2.监听下载进度.
// 1. 文件总大小,当前下载的数据量.
// 直接设置进度条,进度条不能平滑显示下载进度(为什么?)
}
- (void)touchesBegan:(NSSet<UITouch*> *)touches withEvent:(UIEvent*)event
{
NSLog(@"touchesBegan");
// http://dldir1.qq.com/qqfile/QQforMac/QQ_V4.0.4.dmg
//
NSString *urlString = @"http://127.0.0.1/dawenjian.zip";
// 1. 创建请求
NSURL *url = [NSURLURLWithString:urlString];
NSURLRequest *request = [NSURLRequestrequestWithURL:url];
// 下载,代理.
// 创建网络连接,设置代理对象
NSURLConnection *conn = [[NSURLConnectionalloc]initWithRequest:requestdelegate:self];
// 代理对象创建完毕之后,就会自动调用代理方法.不需要手动启动的.
[conn start];
}
#pragma NSURLConnectionDataDelegate
//接收到 响应头信息的时候就会调用.(最先调用的方法.),只会调用一次.
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
NSLog(@"%@ %@",response, [NSThreadcurrentThread]);
// 记录文件总大小
self.expectedLength= response.expectedContentLength;
// 准备下载文件数据之前,实例化文件下载路径.
self.filePath= [NSStringstringWithFormat:@"/Users/teacher/Desktop/%@",response.suggestedFilename];
// 创建管道.
self.stream= [[NSOutputStreamalloc]initToFileAtPath:self.filePathappend:YES];
// 管道是需要手动开启的.
[self.streamopen];
}
//接收到 数据(实体内容)的时候就会调用.也会调用多次.
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
// NSLog(@"本次接收到%ld 的数据 %@",data.length,[NSThread currentThread]);
// 记录已经下载的文件大小.
self.totalBytes+= data.length;
NSLog(@"%lld %lld %@",self.totalBytes,self.expectedLength,[NSThreadcurrentThread]);
self.progressView.progress= (float)self.totalBytes/self.expectedLength;
// 顺着管道,流入数据.
[self.streamwrite:[databytes]maxLength:data.length];
// NSLog(@"%@",data);
}
//网络完成之后(数据下载完毕),就会调用.
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSLog(@"下载完毕...%@",[NSThreadcurrentThread]);
// 关闭管道.
[self.streamclose];
}
@interfaceViewController ()<NSURLConnectionDataDelegate>
//文件下载完毕之后,保存的路径.
@property(nonatomic,copy)NSString*filePath;
//文件输入输出流管道.
@property(nonatomic,strong)NSOutputStream*stream;
//文件总大小
@property(nonatomic,assign)long long expectedLength;
//当前已经下载的数据量
@property(nonatomic,assign)long long totalBytes;
//下载进度条
@property(nonatomic,strong)UIProgressView *progressView;
@end
@implementationViewController
-(UIProgressView*)progressView
{
if (!_progressView) {
_progressView = [[UIProgressViewalloc]initWithFrame:CGRectMake(20,50,335,2)];
// 进度条颜色
_progressView.tintColor= [UIColorgreenColor];
[self.viewaddSubview:_progressView];
}
return _progressView;
}
- (void)viewDidLoad {
[superviewDidLoad];
// 1. 下载过程中,内存不能变大.
// 2.监听下载进度.
// 1. 文件总大小,当前下载的数据量.
// 直接设置进度条,进度条不能平滑显示下载进度(为什么?)
}
- (void)touchesBegan:(NSSet<UITouch*> *)touches withEvent:(UIEvent*)event
{
NSLog(@"touchesBegan");
// http://dldir1.qq.com/qqfile/QQforMac/QQ_V4.0.4.dmg
//
NSString *urlString = @"http://127.0.0.1/dawenjian.zip";
// 1. 创建请求
NSURL *url = [NSURLURLWithString:urlString];
NSURLRequest *request = [NSURLRequestrequestWithURL:url];
// 下载,代理.
// 创建网络连接,设置代理对象
NSURLConnection *conn = [[NSURLConnectionalloc]initWithRequest:requestdelegate:self];
// 代理对象创建完毕之后,就会自动调用代理方法.不需要手动启动的.
[conn start];
}
#pragma NSURLConnectionDataDelegate
//接收到 响应头信息的时候就会调用.(最先调用的方法.),只会调用一次.
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
NSLog(@"%@ %@",response, [NSThreadcurrentThread]);
// 记录文件总大小
self.expectedLength= response.expectedContentLength;
// 准备下载文件数据之前,实例化文件下载路径.
self.filePath= [NSStringstringWithFormat:@"/Users/teacher/Desktop/%@",response.suggestedFilename];
// 创建管道.
self.stream= [[NSOutputStreamalloc]initToFileAtPath:self.filePathappend:YES];
// 管道是需要手动开启的.
[self.streamopen];
}
//接收到 数据(实体内容)的时候就会调用.也会调用多次.
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
// NSLog(@"本次接收到%ld 的数据 %@",data.length,[NSThread currentThread]);
// 记录已经下载的文件大小.
self.totalBytes+= data.length;
NSLog(@"%lld %lld %@",self.totalBytes,self.expectedLength,[NSThreadcurrentThread]);
self.progressView.progress= (float)self.totalBytes/self.expectedLength;
// 顺着管道,流入数据.
[self.streamwrite:[databytes]maxLength:data.length];
// NSLog(@"%@",data);
}
//网络完成之后(数据下载完毕),就会调用.
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSLog(@"下载完毕...%@",[NSThreadcurrentThread]);
// 关闭管道.
[self.streamclose];
}
@end
解决 BUG:直接设置进度条,进度条不能平滑是显示下载进度
下载和刷新进度条都在主线程中,所以把下载进度条放在子线程中操作。优先保证主线程的运行———>还是有坑,解决思路————>关于网络连接放到子线程中处理(还是有坑,因为NSURLConnectDelegate是一个特殊的事件源,要手动开启运行循环,CFRunLoopRun();先添加事件源,再开启运行循环)
- (void)touchesBegan:(NSSet<UITouch*> *)touches withEvent:(UIEvent*)event{
//
// 1. 下载过程中,内存不能变大.
// 2.监听下载进度.
// 1. 文件总大小,当前下载的数据量.
// 直接设置进度条,进度条不能平滑显示下载进度(为什么?)
//把网络连接添加到子线程中,就等于把代理添加到了子线程中。NSUrlConnectionDelegate 是一个特殊的事件源.代理方法想要执行,必须运行循环来执行.手动开启运行循环:先添加事件源,再开启运行循环.
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSString *urlString = @"http://127.0.0.1/dawenjian.zip";
// 1. 创建请求
NSURL *url = [NSURLURLWithString:urlString];
NSURLRequest *request = [NSURLRequestrequestWithURL:url];
// 下载,代理.
// 创建网络连接,设置代理对象
NSURLConnection *conn = [[NSURLConnectionalloc]initWithRequest:requestdelegate:self];
//将代理回调设置子线程。相当于NSDefaltModes模式将定时器添加在线程中.优先保证主线程的运行.
[conn setDelegateQueue:[[NSOperationQueuealloc]init]];
// 代理对象创建完毕之后,就会自动调用代理方法.不需要手动启动的.
[conn start];
// NSUrlConnectionDelegate 是一个特殊的事件源.代理方法想要执行,必须运行循环来执行.
// 手动开启运行循环:先添加事件源,再开启运行循环.
CFRunLoopRun();
// [[NSRunLoop currentRunLoop] run];
});
}
#pragma mark - NSURLConnectionDataDelegate
////接收到 响应头信息的时候就会调用.(最先调用的方法.),只会调用一次.
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response{
// 记录文件总大小
self.expectedLength= response.expectedContentLength;
NSLog(@"响应头信息:%@---%@",response,[NSThreadcurrentThread]);
// // 准备下载文件数据之前,实例化文件下载路径.
self.filePath= [NSStringstringWithFormat:@"/Users/zzx/Desktop/%@",response.suggestedFilename];
//实例化”管道“
self.stream= [NSOutputStreamoutputStreamToFileAtPath:self.filePathappend:YES];
//开启管道
// 管道是需要手动开启的.
[self.streamopen];
}
//接收到 数据(实体内容)的时候就会调用.也会调用多次.
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data{
// NSLog(@"%@ ---%@",[NSThread currentThread],data);
//每次调用,都把数据保存到定义的数据属性中
NSLog(@"本次接收到%ld 的数据 %@",data.length,[NSThreadcurrentThread]);
// 记录已经下载的文件大小.
self.totalBytes+= data.length;
dispatch_async(dispatch_get_main_queue(), ^{
//进度条进度
self.progress.progress= (float)self.totalBytes/self.expectedLength;
});
// 顺着管道,流入数据.
[self.streamwrite:[databytes]maxLength:data.length];
}
//网络完成之后(数据下载完毕),就会调用.
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSLog(@"下载完毕...%@",[NSThreadcurrentThread]);
//关闭管道
[self.streamclose];
}
//
// 1. 下载过程中,内存不能变大.
// 2.监听下载进度.
// 1. 文件总大小,当前下载的数据量.
// 直接设置进度条,进度条不能平滑显示下载进度(为什么?)
//把网络连接添加到子线程中,就等于把代理添加到了子线程中。NSUrlConnectionDelegate 是一个特殊的事件源.代理方法想要执行,必须运行循环来执行.手动开启运行循环:先添加事件源,再开启运行循环.
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSString *urlString = @"http://127.0.0.1/dawenjian.zip";
// 1. 创建请求
NSURL *url = [NSURLURLWithString:urlString];
NSURLRequest *request = [NSURLRequestrequestWithURL:url];
// 下载,代理.
// 创建网络连接,设置代理对象
NSURLConnection *conn = [[NSURLConnectionalloc]initWithRequest:requestdelegate:self];
//将代理回调设置子线程。相当于NSDefaltModes模式将定时器添加在线程中.优先保证主线程的运行.
[conn setDelegateQueue:[[NSOperationQueuealloc]init]];
// 代理对象创建完毕之后,就会自动调用代理方法.不需要手动启动的.
[conn start];
// NSUrlConnectionDelegate 是一个特殊的事件源.代理方法想要执行,必须运行循环来执行.
// 手动开启运行循环:先添加事件源,再开启运行循环.
CFRunLoopRun();
// [[NSRunLoop currentRunLoop] run];
});
}
#pragma mark - NSURLConnectionDataDelegate
////接收到 响应头信息的时候就会调用.(最先调用的方法.),只会调用一次.
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response{
// 记录文件总大小
self.expectedLength= response.expectedContentLength;
NSLog(@"响应头信息:%@---%@",response,[NSThreadcurrentThread]);
// // 准备下载文件数据之前,实例化文件下载路径.
self.filePath= [NSStringstringWithFormat:@"/Users/zzx/Desktop/%@",response.suggestedFilename];
//实例化”管道“
self.stream= [NSOutputStreamoutputStreamToFileAtPath:self.filePathappend:YES];
//开启管道
// 管道是需要手动开启的.
[self.streamopen];
}
//接收到 数据(实体内容)的时候就会调用.也会调用多次.
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data{
// NSLog(@"%@ ---%@",[NSThread currentThread],data);
//每次调用,都把数据保存到定义的数据属性中
NSLog(@"本次接收到%ld 的数据 %@",data.length,[NSThreadcurrentThread]);
// 记录已经下载的文件大小.
self.totalBytes+= data.length;
dispatch_async(dispatch_get_main_queue(), ^{
//进度条进度
self.progress.progress= (float)self.totalBytes/self.expectedLength;
});
// 顺着管道,流入数据.
[self.streamwrite:[databytes]maxLength:data.length];
}
//网络完成之后(数据下载完毕),就会调用.
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSLog(@"下载完毕...%@",[NSThreadcurrentThread]);
//关闭管道
[self.streamclose];
}
3.BUG——>下载过程中,下载到本地的文件不能变大。
下载业务逻辑
{
1. 本地文件大小 > 服务器文件大小 : 1>删除本地文件2>重新开始下载
2. 本地文件大小 < 服务器文件大小 :
{
*1如果本地文件大小= 0 :直接从0开始下载(重新开始下载).
*2如果本地文件大小> 0 :断点续传:
{
设置 Range属性,告诉服务器断点续传开始的位置.
{
1. 本地文件大小 > 服务器文件大小 : 1>删除本地文件2>重新开始下载
2. 本地文件大小 < 服务器文件大小 :
{
*1如果本地文件大小= 0 :直接从0开始下载(重新开始下载).
*2如果本地文件大小> 0 :断点续传:
{
设置 Range属性,告诉服务器断点续传开始的位置.
Range格式:
bytes=x-y 从 x字节开始下载,下载y个字节
bytes=x- 从 x字节开始下载,下载到文件末尾
bytes=-x 从文件开始下载,下载x 字节
设置 Range 属性之后,服务器返回的状态码会变成206.
#import"ViewController.h"
@interfaceViewController ()<NSURLConnectionDataDelegate>
//定义一个数据保存到本地的地址属性
@property(nonatomic,copy)NSString* filePath;
//文件输入输出流
@property(nonatomic,strong)NSOutputStream* steam;
//下载进度条
@property(nonatomic,strong)UIProgressView* progressView;
//已经下载的数据量
@property(nonatomic,assign)longlong totalBtyes;
//文件总数据量
@property(nonatomic,assign)longlong expectedBtyesLength;
//本地文件大小
@property(nonatomic,assign)long long localFileLength;
@end
@implementationViewController
-(UIProgressView*)progressView{
if (!_progressView) {
_progressView = [[ UIProgressView alloc]initWithFrame:CGRectMake(20,50,335,2)];
_progressView.tintColor= [ UIColor redColor];
[self.viewaddSubview:_progressView];
}
return _progressView;
}
- (void)viewDidLoad {
[superviewDidLoad];
NSString *urlString = @"http://127.0.0.1/dawenjian.zip";
self.filePath= @"/Users/zzx/Desktop/dawenjian";
// 1.先检查服务器文件大小.
[selfcheckServerFileWithUrlString:urlString];
// 2. 检查本地文件大小
[selfgetFilepathWithUrlString:self.filePath];
// 主线程中执行UI操作
self.progressView.progress= (float)self.totalBtyes/self.expectedBtyesLength;
}
- (void)touchesBegan:(NSSet<UITouch*> *)touches withEvent:(UIEvent*)event{
NSLog(@"touchesBegan");
NSString *urlString = @"http://127.0.0.1/dawenjian.zip";
// 1. 先检查服务器文件大小.
[selfcheckServerFileWithUrlString:urlString];
// 2. 检查本地文件大小
[selfgetFilepathWithUrlString:self.filePath];
if (self.localFileLength>= self.expectedBtyesLength) {//删除本地文件,并且重新开始下载
NSLog(@"删除本地文件,开始新文件的下载");
// 1. 删除本地文件
[[NSFileManagerdefaultManager]removeItemAtPath:self.filePatherror:NULL];
// 2. 重新开始下载
[selfgetFilepathWithUrlString:urlString];
return;
}
if (self.localFileLength< self.expectedBtyesLength) { // 本地保存的文件小于服务器的文件
if (self.localFileLength> 0) {
NSLog(@"断点续传");
}else
{
NSLog(@"重新开始下载");
}
// 断点续传 (包含了两个过程: 1.从0开始下载2.从断点开始下载)
dispatch_async(dispatch_get_global_queue(0,0), ^{
// 1. 创建请求
NSURL *url = [NSURLURLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequestrequestWithURL:url];
//从X 位置 开始 ,下载到 文件末尾
NSString * range = [NSStringstringWithFormat:@"bytes=%lld-",self.localFileLength];
//告诉服务器,从哪里开始下载
[request setValue:rangeforHTTPHeaderField:@"Range"];
//创建网络连接。设置代理对象
NSURLConnection * conn = [[ NSURLConnection alloc]initWithRequest:requestdelegate:self];
// 将代理回调设置子线程.相当于NSDefaltModes 模式将定时器添加在线程中.优先保证主线程的运行.
[conn setDelegateQueue:[[NSOperationQueuealloc]init]];
[conn start];
// 手动开启运行循环:先添加事件源,再开启运行循环.
CFRunLoopRun();
});
}
}
#pragma mark -检查服务器文件大小( 同步 还是 异步?) --同步请求.
- (void)checkServerFileWithUrlString:(NSString*)urlString{
// 只获取 response ,不要data.
NSURL *url = [NSURLURLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequestrequestWithURL:url];
// 设置请求方法:
// HEAD 是 http 请求规定的方法. file协议没有这个方法.
// 这个方法只获得响应头信息,不会获取具体的文件内容.速度比较快,一般是使用同步请求发送的.
request.HTTPMethod= @"HEAD";
NSURLResponse * response = nil;
[NSURLConnectionsendSynchronousRequest:requestreturningResponse:&responseerror:NULL];
NSLog(@"%lld",response.expectedContentLength);
self.expectedBtyesLength= response.expectedContentLength;
}
#pragma mark -发送本地请求,获知本地文件大小
-(void)getFilepathWithUrlString:(NSString*) urlString{
// 利用 NSFileManager 来检查本地文件大小.
// 获取文件管理器对象
// 检查这个路径下,是否有这个文件.
BOOL is_YES = [[NSFileManagerdefaultManager]fileExistsAtPath:self.filePath];
if (is_YES) {
NSLog(@"本地文件存在");
// 获取本地文件信息,文件信息中不包含文件类型.
NSDictionary * dict = [[ NSFileManager defaultManager]attributesOfItemAtPath:self.filePatherror:NULL];
// 直接通过字典属性取值,得不到数字(得到是对象),没法对比.
NSLog(@"%@",dict);
//记录本地文件的大小
self.localFileLength= [dict[NSFileSize]integerValue];
}else{
self.localFileLength= 0;
NSLog(@"本地文件不存在");
}
}
#pragma mark -NSURLConnectionDataDelegate
//接收响应头信息时会调用,只调用一次
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response{
NSLog(@"%@ %@",response, [NSThreadcurrentThread]);
//获取总数据量
// self.expectedBtyesLength = response.expectedContentLength;
// 实例化接收数据的地址 地址名称随机最优
// self.filePath = [NSString stringWithFormat:@"/Users/zzx/Desktop/%@",response.suggestedFilename];
//实例化管道
self.steam= [ NSOutputStream outputStreamToFileAtPath:self.filePathappend:YES];
//打开管道
[self.steamopen];
}
//发送网络请求,下载数据.
- (void)getServerDataWithUrlString:(NSString*)urlString{
//异步网络请求
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSURL * url =[ NSURL URLWithString:urlString];
NSURLRequest * request = [ NSURLRequest requestWithURL:url];
//设置请求代理
NSURLConnection * conn = [ NSURLConnection connectionWithRequest:requestdelegate:self];
//对代理回调在子线程中执行
[conn setDelegateQueue:[[NSOperationQueuealloc] init]];
// 开启代理方法 ,也会自动开启
[conn start];
// 手动开启运行循环:先添加事件源,再开启运行循环.
CFRunLoopRun();
});
}
//接收数据时会调用,持续调用,直到完成
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data{
// 记录已经下载的文件大小.
self.totalBtyes+= data.length;
NSLog(@"%lld %lld %@",self.localFileLength,self.expectedBtyesLength,[NSThreadcurrentThread]);
dispatch_async(dispatch_get_main_queue(), ^{
self.progressView.progress= (float)self.totalBtyes/self.expectedBtyesLength;
});
// 顺着管道,流入数据.
[self.steamwrite:[databytes]maxLength:data.length];
}
//下载结束后调用,
- (void)connectionDidFinishLoading:(NSURLConnection*)connection{
//关闭管道
[self.steamclose];
}
@end
大文件: 会造成内存暴涨{先将文件下载到内存中(data),然后再写入磁盘}.为了防止内存暴涨,不能直接使用block回调.
0 0
- NSURLConnection 文件下载的BUG及解决思路、方案
- POI处理excel文件中的日期格式数据bug的解决思路及方法
- 挂起bug的解决思路
- Java处理导入excel文件的解决思路和方案
- 网络---大文件的下载(NSURLConnection)
- 文件的下载(通过NSURLConnection代理)
- NSUrlConnection 下载文件
- 通过NSURLConnection下载文件
- NSURLConnection下载大文件
- ASP.NET 大文件下载的实现思路及代码
- ASP.NET 大文件下载的实现思路及代码
- 项目中bug的解决思路
- 项目中bug的解决思路二
- Android解决bug的思路:追本溯源
- NSURLConnection实现大文件下载
- NSURLConnection 下载文件增强版
- IOS - NSURLConnection大文件下载
- NSURLConnection下载一个大文件
- Android Studio 常用插件
- Android Fragment切换 和 数据懒加载的分离处理
- H5页面设计
- http请求和数据丢失问题
- AJAX
- NSURLConnection 文件下载的BUG及解决思路、方案
- PorterDuffXferMode不正确的真正原因PorterDuffXferMode深入试验)
- oracle----------数据类型,增删改
- 1.XML 简介
- poj 1979 Red and Black
- Quartz 2D
- Sudoku Solver
- 数字字符串四则运算
- Android 动画学习