0922-网络 文件下载
------------------------------
工作中不要用NSURLConnection NSURLSession(可以更好的理解AFN) 用AFN(对这2个类分别做了封装)就可以了
掌握程度: 有的公司旧的项目还用NSURLConnection 自动异步 [NSURLConnection connectionWithRequest:request delegate:self]; 比sendAsync sync好 可以监听这个请求过程
1、知道md5就可以了
// HMViewController.m #import "HMViewController.h" #import "MBProgressHUD+MJ.h" #import "NSString+Hash.h" @interface HMViewController () @property (weak, nonatomic) IBOutlet UITextField *username; @property (weak, nonatomic) IBOutlet UITextField *pwd; - (IBAction)login; @end @implementation HMViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self.view endEditing:YES]; } - (IBAction)login { // 1.用户名 NSString *usernameText = self.username.text; if (usernameText.length == 0) { [MBProgressHUD showError:@"请输入用户名"]; return; } // 2.密码 NSString *pwdText = self.pwd.text; if (pwdText.length == 0) { [MBProgressHUD showError:@"请输入密码"]; return; } // 增加蒙板 [MBProgressHUD showMessage:@"正在拼命登录中...."]; // 3.发送用户名和密码给服务器(走HTTP协议) // 创建一个URL : 请求路径 NSURL *url = [NSURL URLWithString:@"http://192.168.15.172:8080/MJServer/login"]; // 创建一个请求 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // 5秒后算请求超时(默认60s超时) request.timeoutInterval = 15; request.HTTPMethod = @"POST"; #warning 对pwdText进行加密 pwdText = [self MD5Reorder:pwdText]; // 设置请求体 NSString *param = [NSString stringWithFormat:@"username=%@&pwd=%@", usernameText, pwdText]; NSLog(@"%@", param); // NSString --> NSData request.HTTPBody = [param dataUsingEncoding:NSUTF8StringEncoding]; // 设置请求头信息 [request setValue:@"iPhone 6" forHTTPHeaderField:@"User-Agent"]; // 发送一个同步请求(在主线程发送请求) // queue :存放completionHandler这个任务 NSOperationQueue *queue = [NSOperationQueue mainQueue]; [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler: ^(NSURLResponse *response, NSData *data, NSError *connectionError) { // 隐藏蒙板 [MBProgressHUD hideHUD]; // 这个block会在请求完毕的时候自动调用 if (connectionError || data == nil) { // 一般请求超时就会来到这 [MBProgressHUD showError:@"请求失败"]; return; } // 解析服务器返回的JSON数据 NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil]; NSString *error = dict[@"error"]; if (error) { [MBProgressHUD showError:error]; } else { NSString *success = dict[@"success"]; [MBProgressHUD showSuccess:success]; } }]; } /** * MD5($pass.$salt) * * @param text 明文 * * @return 加密后的密文 */ - (NSString *)MD5Salt:(NSString *)text { // 撒盐:随机地往明文中插入任意字符串 NSString *salt = [text stringByAppendingString:@"aaa"]; return [salt md5String]; } /** * MD5(MD5($pass)) * * @param text 明文 * * @return 加密后的密文 */ - (NSString *)doubleMD5:(NSString *)text { return [[text md5String] md5String]; } /** * 先加密,后乱序 * * @param text 明文 * * @return 加密后的密文 */ - (NSString *)MD5Reorder:(NSString *)text { NSString *pwd = [text md5String]; // 加密后pwd == 3f853778a951fd2cdf34dfd16504c5d8 NSString *prefix = [pwd substringFromIndex:2]; NSString *subfix = [pwd substringToIndex:2]; // 乱序后 result == 853778a951fd2cdf34dfd16504c5d83f NSString *result = [prefix stringByAppendingString:subfix]; NSLog(@"\ntext=%@\npwd=%@\nresult=%@", text, pwd, result); return result; } @end
2、掌握Reachability类 监听网络连接 发送到通知中心(以后用AFN):
通知中心添加observer:self 监听这个通知 kReachabilityChangedNotification 执行 netWorkConnectChange
Reachability -reachabilityForInternetConnection -startNotifier
netWorkConnectChange{
if([[Reachability reachabilityForLocalWiFi] currentReachabilityStatus] != NotReachable) return @"wifi";
if([[Reachability reachabilityForInternetConnection] currentReachabilityStatus] != NotReachable) return @"自带的3G或者4G";
}
- (void)dealloc
{
[self.reachability stopNotifier];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
// // HMViewController.m #import "HMViewController.h" #import "Reachability.h" #import "HMNetworkTool.h" @interface HMViewController () @property (nonatomic, strong) Reachability *reachability; @end @implementation HMViewController - (void)viewDidLoad { [super viewDidLoad]; // UIProgressView // 监听网络状态发生改变的通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkStateChange) name:kReachabilityChangedNotification object:nil]; // 获得Reachability对象 self.reachability = [Reachability reachabilityForInternetConnection]; // 开始监控网络 [self.reachability startNotifier]; // // 1.获得Reachability对象 // Reachability *wifi = [Reachability reachabilityForLocalWiFi]; // // // 2.获得Reachability对象的当前网络状态 // NetworkStatus wifiStatus = wifi.currentReachabilityStatus; // if (wifiStatus != NotReachable) { // NSLog(@"是WIFI"); // } } - (void)dealloc { [self.reachability stopNotifier]; [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)networkStateChange { NSLog(@"网络状态改变了"); [self checkNetworkState]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self checkNetworkState]; } /** * 监测网络状态 */ - (void)checkNetworkState { if ([HMNetworkTool isEnableWIFI]) { NSLog(@"WIFI环境"); } else if ([HMNetworkTool isEnable3G]) { NSLog(@"手机自带网络"); } else { NSLog(@"没有网络"); } } @end
// // HMNetworkTool.m #import "HMNetworkTool.h" #import "Reachability.h" @implementation HMNetworkTool // 是否WIFI + (BOOL)isEnableWIFI { return ([[Reachability reachabilityForLocalWiFi] currentReachabilityStatus] != NotReachable); } // 是否3G + (BOOL)isEnable3G { return ([[Reachability reachabilityForInternetConnection] currentReachabilityStatus] != NotReachable); } @end
3、小文件下载 掌握
// // HMViewController.m #import "HMViewController.h" @interface HMViewController () @end @implementation HMViewController - (void)viewDidLoad { [super viewDidLoad]; // 下载小文件的方式 // 1.NSData dataWithContentsOfURL // 2.NSURLConnection } // 1.NSData dataWithContentsOfURL - (void)downloadFile { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 其实这就是一个GET请求 NSURL *url = [NSURL URLWithString:@"http://localhost:8080/MJServer/resources/images/minion_01.png"]; NSData *data = [NSData dataWithContentsOfURL:url]; NSLog(@"%d", data.length); }); } // 2.NSURLConnection - (void)downloadFile2 { NSURL *url = [NSURL URLWithString:@"http://localhost:8080/MJServer/resources/images/minion_01.png"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { NSLog(@"%d", data.length); }]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self downloadFile2]; } @end
4、大文件下载不合理 不掌握 用一个变量接收完整的文件数据 内存占用太大
5、大文件下载 (合理 掌握)
0、[NSURLConnection connectionWithRequest: delegate:] 监听到3个步骤 让代理执行
1、-connection didReceiveResponse , -connection didReceiveData , -connection connectionDidFinishLoading
-connection didReceiveResponse : NSFileManager 创建空文件, [NSFileHandle fileHandleForWritingAtPath:filepath];
-connection -didReceiveData : NSFileHandle seekToEndOfFile(移动到文件尾部) writeData
-connection connectionDidFinishLoading : 停止并清空NSFileHandle NSFileHandle closeFile NSFileHandle nil;
// // HMViewController.m #import "HMViewController.h" #import "DACircularProgressView.h" @interface HMViewController () <NSURLConnectionDataDelegate> /** * 用来写数据的文件句柄对象 */ @property (nonatomic, strong) NSFileHandle *writeHandle; /** * 文件的总大小 */ @property (nonatomic, assign) long long totalLength; /** * 当前已经写入的文件大小 */ @property (nonatomic, assign) long long currentLength; @property (nonatomic, weak) DACircularProgressView *circleView; @end @implementation HMViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. DACircularProgressView *circleView = [[DACircularProgressView alloc] init]; circleView.frame = CGRectMake(100, 50, 100, 100); circleView.progressTintColor = [UIColor redColor]; circleView.trackTintColor = [UIColor blueColor]; circleView.progress = 0.01; [self.view addSubview:circleView]; self.circleView = circleView; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // 1.URL NSURL *url = [NSURL URLWithString:@"http://localhost:8080/MJServer/resources/music.zip"]; // 2.请求 NSURLRequest *request = [NSURLRequest requestWithURL:url]; // 3.下载(创建完conn对象后,会自动发起一个异步请求) [NSURLConnection connectionWithRequest:request delegate:self]; } #pragma mark - NSURLConnectionDataDelegate代理方法 /** * 请求失败时调用(请求超时、网络异常) * * @param error 错误原因 */ - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"didFailWithError"); } /** * 1.接收到服务器的响应就会调用 * * @param response 响应 */ - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { // 文件路径 NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; NSString *filepath = [caches stringByAppendingPathComponent:@"videos.zip"]; // 创建一个空的文件 到 沙盒中 NSFileManager *mgr = [NSFileManager defaultManager]; [mgr createFileAtPath:filepath contents:nil attributes:nil]; // 创建一个用来写数据的文件句柄 self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:filepath]; // 获得文件的总大小 self.totalLength = response.expectedContentLength; } /** * 2.当接收到服务器返回的实体数据时调用(具体内容,这个方法可能会被调用多次) * * @param data 这次返回的数据 */ - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { // 移动到文件的最后面 [self.writeHandle seekToEndOfFile]; // 将数据写入沙盒 [self.writeHandle writeData:data]; // 累计文件的长度 self.currentLength += data.length; NSLog(@"下载进度:%f", (double)self.currentLength/ self.totalLength); self.circleView.progress = (double)self.currentLength/ self.totalLength; } /** * 3.加载完毕后调用(服务器的数据已经完全返回后) */ - (void)connectionDidFinishLoading:(NSURLConnection *)connection { self.currentLength = 0; self.totalLength = 0; // 关闭文件 [self.writeHandle closeFile]; self.writeHandle = nil; } @end
6、断点下载 暂停后再开始 指定下载起始位置为记录的下载位置 请求头加range 点击暂停时候 self.conn cancel self.conn=nil
自己补充当下次开始下载的时候从沙盒取出currentLength
sender.selected = !sender.isSelected; if (sender.selected) { // 继续(开始)下载 // 1.URL NSURL *url = [NSURL URLWithString:@"http://localhost:8080/MJServer/resources/videos.zip"]; // 2.请求 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // 设置请求头 NSString *range = [NSString stringWithFormat:@"bytes=%lld-", self.currentLength]; [request setValue:range forHTTPHeaderField:@"Range"]; // 3.下载(创建完conn对象后,会自动发起一个异步请求) self.conn = [NSURLConnection connectionWithRequest:request delegate:self]; } else { // 暂停 [self.conn cancel]; self.conn = nil; }
明天的多线程断点下载 以后拿来用就可以了 不需要掌握的 太复杂
// HMViewController.m #import "HMViewController.h" #import "DACircularProgressView.h" @interface HMViewController () <NSURLConnectionDataDelegate> - (IBAction)download:(UIButton *)sender; /** * 用来写数据的文件句柄对象 */ @property (nonatomic, strong) NSFileHandle *writeHandle; /** * 文件的总大小 */ @property (nonatomic, assign) long long totalLength; /** * 当前已经写入的文件大小 */ @property (nonatomic, assign) long long currentLength; /** * 连接对象 */ @property (nonatomic, strong) NSURLConnection *conn; @property (nonatomic, weak) DACircularProgressView *circleView; @end @implementation HMViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. DACircularProgressView *circleView = [[DACircularProgressView alloc] init]; circleView.frame = CGRectMake(100, 50, 100, 100); circleView.progressTintColor = [UIColor redColor]; circleView.trackTintColor = [UIColor blueColor]; circleView.progress = 0.0000001; [self.view addSubview:circleView]; self.circleView = circleView; } #pragma mark - NSURLConnectionDataDelegate代理方法 /** * 请求失败时调用(请求超时、网络异常) * * @param error 错误原因 */ - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"didFailWithError"); } /** * 1.接收到服务器的响应就会调用 * * @param response 响应 */ - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { if (self.currentLength) return; // 文件路径 NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; NSString *filepath = [caches stringByAppendingPathComponent:@"videos.zip"]; // 创建一个空的文件 到 沙盒中 NSFileManager *mgr = [NSFileManager defaultManager]; [mgr createFileAtPath:filepath contents:nil attributes:nil]; // 创建一个用来写数据的文件句柄 self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:filepath]; // 获得文件的总大小 self.totalLength = response.expectedContentLength; } /** * 2.当接收到服务器返回的实体数据时调用(具体内容,这个方法可能会被调用多次) * * @param data 这次返回的数据 */ - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { // 移动到文件的最后面 [self.writeHandle seekToEndOfFile]; // 将数据写入沙盒 [self.writeHandle writeData:data]; // 累计文件的长度 self.currentLength += data.length; self.circleView.progress = (double)self.currentLength/ self.totalLength; } /** * 3.加载完毕后调用(服务器的数据已经完全返回后) */ - (void)connectionDidFinishLoading:(NSURLConnection *)connection { self.currentLength = 0; self.totalLength = 0; // 关闭文件 [self.writeHandle closeFile]; self.writeHandle = nil; } - (IBAction)download:(UIButton *)sender { // 状态取反 sender.selected = !sender.isSelected; // 断点续传 // 断点下载 if (sender.selected) { // 继续(开始)下载 // 1.URL NSURL *url = [NSURL URLWithString:@"http://localhost:8080/MJServer/resources/videos.zip"]; // 2.请求 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // 设置请求头 NSString *range = [NSString stringWithFormat:@"bytes=%lld-", self.currentLength]; [request setValue:range forHTTPHeaderField:@"Range"]; // 3.下载(创建完conn对象后,会自动发起一个异步请求) self.conn = [NSURLConnection connectionWithRequest:request delegate:self]; } else { // 暂停 [self.conn cancel]; self.conn = nil; } } @end
7、NSURLSession ios7新推出的 目的是为了取代NSURLConnection 自动写到沙盒temp文件里
session 交给代理来监听下载进度 。 [session dataTask] [dataTask start]。[session downloadTask] [task start]
NSURLSession与NSURLConnection比较如下:
[NSURLRequest requestWithURL:URL]; [NSURLConnection connectionWithRequest:request delegate:self] 等同于下面4个 NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:[NSOperationQueue mainQueue]]; NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url]; [task resume]; NSURLSessionDownloadDelegate 1、URLSession downloadTask -didFinishDownloadingToURL(NSURL *)location 已经下到temp里(location.path) 获取 temp临时文件路 ( location.path ) 移动到的文件路径 caches/下载的文件名( downloadTask.response.suggestedFilename ) NSFileManager移动到 2、URLSession downloadTask -didResumeAtOffset 恢复下载时候调用 一般永不上这个方法 3、URLSession downloadTask -didWriteData:(int64_t)bytesWritten 这次下载的长度 已经下载的长度 总长度
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^ 相当于下面的 NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler [task resume]; 或者 NSURLSessionDataTask *task = [session dataTaskWithRequest [task resume];
// // HMViewController.m #import "HMViewController.h" @interface HMViewController () <NSURLSessionDownloadDelegate> @end @implementation HMViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } // 任务:任何请求都是一个任务 // NSURLSessionDataTask : 普通的GET\POST请求 // NSURLSessionDownloadTask : 文件下载 // NSURLSessionUploadTask : 文件上传 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self downloadTask2]; } - (void)downloadTask2 { NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration]; // 1.得到session对象 NSURLSession *session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:[NSOperationQueue mainQueue]]; // 2.创建一个下载task NSURL *url = [NSURL URLWithString:@"http://localhost:8080/MJServer/resources/test.mp4"]; NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url]; // 3.开始任务 [task resume]; // 如果给下载任务设置了completionHandler这个block,也实现了下载的代理方法,优先执行block } #pragma mark - NSURLSessionDownloadDelegate /** * 下载完毕后调用 * * @param location 临时文件的路径(下载好的文件) */ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { // location : 临时文件的路径(下载好的文件) NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; // response.suggestedFilename : 建议使用的文件名,一般跟服务器端的文件名一致 NSString *file = [caches stringByAppendingPathComponent:downloadTask.response.suggestedFilename]; // 将临时文件剪切或者复制Caches文件夹 NSFileManager *mgr = [NSFileManager defaultManager]; // AtPath : 剪切前的文件路径 // ToPath : 剪切后的文件路径 [mgr moveItemAtPath:location.path toPath:file error:nil]; } /** * 恢复下载时调用 */ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes { } /** * 每当下载完(写完)一部分时就会调用(可能会被调用多次) * * @param bytesWritten 这次调用写了多少 * @param totalBytesWritten 累计写了多少长度到沙盒中了 * @param totalBytesExpectedToWrite 文件的总长度 */ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { double progress = (double)totalBytesWritten / totalBytesExpectedToWrite; NSLog(@"下载进度---%f", progress); } #pragma mark ------------------------------------------------------------------ /** * 下载任务:不能看到下载进度 */ - (void)downloadTask { // 1.得到session对象 NSURLSession *session = [NSURLSession sharedSession]; // 2.创建一个下载task NSURL *url = [NSURL URLWithString:@"http://localhost:8080/MJServer/resources/test.mp4"]; NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { // location : 临时文件的路径(下载好的文件) NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; // response.suggestedFilename : 建议使用的文件名,一般跟服务器端的文件名一致 NSString *file = [caches stringByAppendingPathComponent:response.suggestedFilename]; // 将临时文件剪切或者复制Caches文件夹 NSFileManager *mgr = [NSFileManager defaultManager]; // AtPath : 剪切前的文件路径 // ToPath : 剪切后的文件路径 [mgr moveItemAtPath:location.path toPath:file error:nil]; }]; // 3.开始任务 [task resume]; } - (void)dataTask { // 1.得到session对象 NSURLSession *session = [NSURLSession sharedSession]; // 2.创建一个task,任务 // NSURL *url = [NSURL URLWithString:@"http://localhost:8080/MJServer/video"]; // NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { // NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil]; // NSLog(@"----%@", dict); // }]; NSURL *url = [NSURL URLWithString:@"http://192.168.15.172:8080/MJServer/login"]; // 创建一个请求 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.HTTPMethod = @"POST"; // 设置请求体 request.HTTPBody = [@"username=123&pwd=123" dataUsingEncoding:NSUTF8StringEncoding]; NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil]; NSLog(@"----%@", dict); }]; // 3.开始任务 [task resume]; } @end
--------------------------------------
8、NSURLSession文件断点续传 完整的 以后可以用这个
[self.task resume];表示开始任务 而不是恢复任务
self resume111才便是开始回复
多线程断点下载 复杂度极高 没有视频解说 只说了思路: 先创建一个大的文件 四个线程确定每个线程的下载 range 这样下载完就直接是一个完整的文件了
- (IBAction)download:(UIButton *)sender { // 按钮状态取反 sender.selected = !sender.isSelected; if (self.task == nil) { // 开始(继续)下载 if (self.resumeData) { // 恢复 [self resume111]; } else { // 开始 [self startOriginTask111]; } } else { // 暂停 [self pause111]; } }
/** * 恢复(继续) */ - (void)resume111 { // 传入上次暂停下载返回的数据,就可以恢复下载 self.task = [self.session downloadTaskWithResumeData:self.resumeData]; // 开始任务 [self.task resume]; // 清空 self.resumeData = nil; } /** * 暂停 */ - (void)pause111 { __weak typeof(self) vc = self; [self.task cancelByProducingResumeData:^(NSData *resumeData) { // resumeData : 包含了继续下载的开始位置\下载的url vc.resumeData = resumeData; vc.task = nil; }]; }
// // HMViewController.m #import "HMViewController.h" @interface HMViewController () <NSURLSessionDownloadDelegate, NSURLSessionDataDelegate> @property (weak, nonatomic) IBOutlet UIProgressView *progressView; - (IBAction)download:(UIButton *)sender; @property (nonatomic, strong) NSURLSessionDownloadTask *task; @property (nonatomic, strong) NSData *resumeData; @property (nonatomic, strong) NSURLSession *session; @end @implementation HMViewController - (NSURLSession *)session { if (!_session) { // 获得session NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration]; self.session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:[NSOperationQueue mainQueue]]; } return _session; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (IBAction)download:(UIButton *)sender { // 按钮状态取反 sender.selected = !sender.isSelected; if (self.task == nil) { // 开始(继续)下载 if (self.resumeData) { // 恢复 [self resume111]; } else { // 开始 [self startOriginTask111]; } } else { // 暂停 [self pause111]; } } /** * 从零开始 */ - (void)startOriginTask111 { // 1.创建一个下载任务 NSURL *url = [NSURL URLWithString:@"http://192.168.15.172:8080/MJServer/resources/videos/minion_01.mp4"]; self.task = [self.session downloadTaskWithURL:url]; // 2.开始任务 [self.task resume]; } /** * 恢复(继续) */ - (void)resume111 { // 传入上次暂停下载返回的数据,就可以恢复下载 self.task = [self.session downloadTaskWithResumeData:self.resumeData]; // 开始任务 [self.task resume]; // 清空 self.resumeData = nil; } /** * 暂停 */ - (void)pause111 { __weak typeof(self) vc = self; [self.task cancelByProducingResumeData:^(NSData *resumeData) { // resumeData : 包含了继续下载的开始位置\下载的url vc.resumeData = resumeData; vc.task = nil; }]; } #pragma mark - NSURLSessionDownloadDelegate - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; // response.suggestedFilename : 建议使用的文件名,一般跟服务器端的文件名一致 NSString *file = [caches stringByAppendingPathComponent:downloadTask.response.suggestedFilename]; // 将临时文件剪切或者复制Caches文件夹 NSFileManager *mgr = [NSFileManager defaultManager]; // AtPath : 剪切前的文件路径 // ToPath : 剪切后的文件路径 [mgr moveItemAtPath:location.path toPath:file error:nil]; } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { NSLog(@"获得下载进度--%@", [NSThread currentThread]); // 获得下载进度 self.progressView.progress = (double)totalBytesWritten / totalBytesExpectedToWrite; } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes { } //- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data //{ // //} // //- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler //{ // //} @end
————————————————————————————————————————————————————————————
MD5方法在我的微云里
—————————
Reachability 我的微云里有这个框架
创建Reachablity对象并调用 start方法 并作为控制器属性 不让这个对象消失
这个对象会不停监听网络状态并发出notification 到通知中心
我们在通知中心添加一个observer 也就是self
我们拿到通知 做相应的操作
控制器 dealloc 用通知中心移除observer self reachability对象 stop停止发通知
掌握Reachability类(苹果开放了实现方法的类)有助于了解AFN AFN更好用
Reachability.m里有release操作 所以需要
xcode->项目-》build phases-》compile sources Reachability.m 添加上 -fno-objc-arc
Reachability(苹果开放了实现方法的类 有release代码 所以需要
xcode->项目-》build phases-》compile sources Reachability.m 添加上 -fno-objc-arc
) 存放到第三方框架里
-----------
小文件文件下载
NSURLConnection可发 sendAsync异步发送请求 回到某个线程:queue 做什么操作 用于小文件下载 因为大文件用户可能断网
———————
大文件下载 NSURLConnection 合理部分
NSURLConnection connectionWithRequest: delegate: NSURLConnection接收到数据会让代理调用相应的方法
代理需要实现 NSURLConnection写的代理协议
<NSURLConnectionDataDelegate> delegate
NSURLConnection默认都是异步请求
————————
断点下载
——————————
NSURLSession 边下载边写数据
对比NSURLConnection NSURLSession不用设计属性计算文件下载多少
NSURLSessionUploadTask 一般的HTTP服务器不支持苹果的这个功能 所以不需要掌握
int64_4 相当于64位的long long类型