网络篇 - 03.NSURLConnection应用场景
来源:互联网 发布:matlab中矩阵运算 编辑:程序博客网 时间:2024/06/06 07:47
1.小文件下载
- 如果文件比较小,下载方式会比较多
- 直接用NSData的+ (id)dataWithContentsOfURL:(NSURL *)url;
- 利用NSURLConnection发送一个HTTP请求去下载
- 如果是下载图片,还可以利用SDWebImage框架
// 小文件下载-(void)smallDataDownload{ // 1.创建URL NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"]; // 2.根据URL创建URL请求对象 NSURLRequest *request = [NSURLRequest requestWithURL:url]; // 3.发送异步请求 [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { // 4.请求完毕后的回调,得到下载的数据 UIImage *image = [UIImage imageWithData:data]; NSLog(@"%@",image); }];}
2.大文件下载(同时实现断点下载功能)
- 大文件由于需要较长时间,需要监听其具体下载过程,给用户反馈下载进度,提供良好的用户体验
- 由于用户不小心关掉程序,重新打开后,用户是希望此前的下载任务能够接着上次的下载进度继续下载的,所以这里也实现了断点下载功能
3.利用NSFileHandle实现大文件下载
- 这里先使用NSFileHandle实现文件的下载到本地
#import "ViewController.h"#import "NSString+SandboxPath.h"@interface ViewController ()<NSURLConnectionDataDelegate>@property (weak, nonatomic) IBOutlet UIProgressView *progress;@property (nonatomic, strong) NSString *path;// 文件路径@property (nonatomic, assign) NSInteger totalDataSize;// 文件总大小@property (nonatomic, assign) NSInteger curDataLength; // 当前已经下载的文件大小@property (nonatomic, strong) NSFileHandle *fileHandle; // 文件读写操作类@end@implementation ViewController#pragma mark - lazy-(NSFileHandle *)fileHandle{ if (!_fileHandle) { _fileHandle = [NSFileHandle fileHandleForWritingAtPath:self.path]; } return _fileHandle;}- (void)viewDidLoad { [super viewDidLoad]; // 设置文件保存路径 self.path = [@"minion_01.mp4" cacheDir]; NSLog(@"%@",self.path); // 加载文件 [self largeDataDownLoad];}// 大文件下载-(void)largeDataDownLoad{ // 1.创建URL NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"]; // 2.根据URL创建请求对象 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // 2.1设置请求头从哪里开始下载 // 获取当前下载文件的大小 NSFileManager *manager =[NSFileManager defaultManager]; // 获取当前文件属性 NSDictionary *dict = [manager attributesOfItemAtPath:self.path error:nil]; // 获取当前文件大小 NSString *range = [NSString stringWithFormat:@"bytes:%zd-",[dict[NSFileSize] intValue]]; self.curDataLength = [dict[NSFileSize] intValue]; NSLog(@"%@",range); // 设置文件下载的范围(范围为当前下载大小到末尾) [request setValue:range forHTTPHeaderField:@"Range"]; // 2.2发送请求 [NSURLConnection connectionWithRequest:request delegate:self];}#pragma mark - NSURLConnectionDataDelegate// 接收到服务器的响应后调用-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ // 获取文件总大小 NSInteger totalDataSize = response.expectedContentLength + self.curDataLength; self.totalDataSize = totalDataSize; // 创建空文件,用来保存下载的文件,使用文件读写操作类写入下载的数据 NSFileManager *manager = [NSFileManager defaultManager]; // 判断若是文件为空才下创建新文件 if (self.curDataLength == 0) { [manager createFileAtPath:self.path contents:nil attributes:nil]; NSLog(@"创建文件成功"); }}// 每次接收到服务器的数据后调用(会调用一次或多次)-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ // 获取当前已经下载的总大小 self.curDataLength += data.length; // 更新进度条 self.progress.progress = 1.0 * self.curDataLength / self.totalDataSize; NSLog(@"%f",self.progress.progress); // 每次下载都移动到当前已下载文件的末尾 [self.fileHandle seekToEndOfFile]; // 写入数据 [self.fileHandle writeData:data];}// 接收数据完成时调用-(void)connectionDidFinishLoading:(NSURLConnection *)connection{ NSLog(@"%s",__func__); // 下载完成后关闭handle和清空当前下载数据 [self.fileHandle closeFile]; self.curDataLength = 0;}// 接收数据失败调用-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{ NSLog(@"%s",__func__);}
4.利用输出流实现文件的下载和断点下载(NSOutputStream)
#import "ViewController.h"#import "NSString+SandboxPath.h"@interface ViewController ()<NSURLConnectionDataDelegate>@property (weak, nonatomic) IBOutlet UIProgressView *progress;@property (nonatomic, strong) NSString *path;// 文件路径@property (nonatomic, assign) NSInteger totalDataSize;// 文件总大小@property (nonatomic, assign) NSInteger curDataLength; // 当前已经下载的文件大小@property (nonatomic, strong) NSOutputStream *outputStream; // 输出流@end@implementation ViewController#pragma mark - lazy-(NSOutputStream *)outputStream{ if (!_outputStream) { /* 第一个参数: 告诉系统数据流需要输出到哪个文件中 第二个参数: 如果传入YES, 代表每次都在上一次的后面追加 如果传入NO, 代表每次都从头开始 */ _outputStream = [NSOutputStream outputStreamToFileAtPath:self.path append:YES]; // 注意: 如果想利用输出流写入数据, 一定要打开数据流 // 如果数据流打开的文件不存在, 那么会自动创建个新的 [_outputStream open]; } return _outputStream;}- (void)viewDidLoad { [super viewDidLoad]; // 设置文件保存路径 self.path = [@"minion_01.mp4" cacheDir]; NSLog(@"%@",self.path); [self largeDataDownLoad];}// 大文件下载-(void)largeDataDownLoad{ // 1.创建URL NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"]; // 2.根据URL创建请求对象 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // 2.1设置请求头从哪里开始下载 // 获取当前下载文件的大小 NSFileManager *manager =[NSFileManager defaultManager]; NSDictionary *dict = [manager attributesOfItemAtPath:self.path error:nil]; NSString *range = [NSString stringWithFormat:@"bytes:%zd-",[dict[NSFileSize] intValue]]; self.curDataLength = [dict[NSFileSize] intValue]; NSLog(@"%@",range); [request setValue:range forHTTPHeaderField:@"Range"]; // 2.2发送请求 [NSURLConnection connectionWithRequest:request delegate:self];}#pragma mark - NSURLConnectionDataDelegate// 接收到服务器的响应后调用-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ // 获取文件总大小 NSInteger totalDataSize = response.expectedContentLength + self.curDataLength; self.totalDataSize = totalDataSize;}// 每次接收到服务器的数据后调用(会调用一次或多次)-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ // 利用输出流往文件中写数据 [self.outputStream write:data.bytes maxLength:data.length]; // 获取当前已经下载的总大小 self.curDataLength += data.length; // 更新进度条 self.progress.progress = 1.0 * self.curDataLength / self.totalDataSize; NSLog(@"%f",self.progress.progress);}// 接收数据完成时调用-(void)connectionDidFinishLoading:(NSURLConnection *)connection{ NSLog(@"%s",__func__); // 下载完成后关闭输出流和清空当前下载数据 [self.outputStream close]; self.curDataLength = 0;}// 接收数据失败调用-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{ NSLog(@"%s",__func__);}
5.文件上传
- 文件上传需要设置请求头和请求体的格式,并且格式固定如下格式,一步都不能少,不然无法上传成功,后面会讲解AFN框架的时候,AFN已经将这些代码封装好了,我们也无需写这些复杂的代码段
// 1.设置请求头[request setValue:@"multipart/form-data; boundary=分割线" forHTTPHeaderField:@"Content-Type"];// 2.设置请求体非文件参数--分割线\r\nContent-Disposition: form-data; name="参数名"\r\n\r\n参数值\r\n文件参数--分割线\r\nContent-Disposition: form-data; name="参数名"; filename="文件名"\r\nContent-Type: 文件的MIMEType\r\n\r\n文件数据\r\n参数结束的标记--分割线--
- 文件上传实例
#import "ViewController.h"// 请求头的分割线#define ZJHeaderBoundary @"----MrRight"// 请求体的分割线#define ZJBoundary [@"------MrRight" dataUsingEncoding:NSUTF8StringEncoding]// 结束符#define ZJEndBoundary [@"------MrRight--" dataUsingEncoding:NSUTF8StringEncoding]// 将字符串转换为二进制#define ZJEncode(str) [str dataUsingEncoding:NSUTF8StringEncoding]// 换行#define ZJNewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]@interface ViewController ()@end@implementation ViewController-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ // 1.创建URL NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/upload"]; // 2.根据URL创建请求对象 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // 2.1设置请求头 request.HTTPMethod = @"POST"; NSString *temp = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", ZJHeaderBoundary]; [request setValue:temp forHTTPHeaderField:@"Content-Type"]; // 2.2设置请求体 NSMutableData *data = [NSMutableData data]; // 拼接请求体格式 // 2.2.1设置文件参数 [data appendData:ZJBoundary]; [data appendData:ZJNewLine]; // name : 对应服务端接收的字段类型(服务端参数的名称) // filename: 告诉服务端当前的文件的文件名称(也就是告诉服务器用什么名称保存当前上传的文件) [data appendData:[@"Content-Disposition: form-data; name=\"file\"; filename=\"abc.png\"" dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:ZJNewLine]; // 注意点: 文件的类型不一样, 那么content-type的值也不一样 // 1.如果不知道数据时什么类型, 直接传application/octet-stream即可 // application/octet-stream是万能类型 // 数据的Content-Type类型我们称之为MIMETYPE [data appendData:[@"Content-Type: application/octet-stream" dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:ZJNewLine]; [data appendData:ZJNewLine]; // 文件数据 UIImage *image = [UIImage imageNamed:@"abc"]; NSData *imageData = UIImagePNGRepresentation(image); [data appendData:imageData]; [data appendData:ZJNewLine]; [data appendData:ZJNewLine]; // 2.2.2设置非文件参数 [data appendData:ZJBoundary]; [data appendData:ZJNewLine]; // name : 对应服务端接收的字段类型(服务端参数的名称) [data appendData:[@"Content-Disposition: form-data; name=\"username\"" dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:ZJNewLine]; [data appendData:ZJNewLine]; [data appendData:[@"zj" dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:ZJNewLine]; // 2.2.3设置结束符号 [data appendData:ZJEndBoundary]; request.HTTPBody = data; // 3.发送请求 [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); }];}
6.NSURLConnection和NSRunLoop
- 通过代理监控NSURLConnection请求,以及请求过程设置在哪个线程执行
- 通过alloc/init方法创建NSURLConnection,会自动发送异步网络请求
- 只要利用NSURLConnection发送一个请求, 那么系统会就自动将NSURLConnection加入到当前线程的RunLoop中
- 如果是在主线程中发送求情, 那么主线程的RunLoop永远存在, 所以NSURLConnection不会被释放,所以能够执行监听请求操作
- 在主线程中发送请求
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; // alloc/init方法会自动发送请求,默认发送异步请求(默认是在主线程中执行) //NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self]; // startImmediately:若为YES,则会马上发送异步请求,若为NO,需要手动启动发送请求 NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; // 发送请求 [conn start]; // 设置代理方法回调的线程(设置在子线程中调用) [conn setDelegateQueue:[[NSOperationQueue alloc] init]];
- 如果是在子线程中发送请求, 那么子线程默认没有RunLoop, 所以NSURLConnection会被释放,所以无法监听请求操作
- 所以要想在子线程中发送的请求能够被监听,若是使用alloc/init方法,就必须自己创建RunLoop对象,并且在设置监听时运行RunLoop,这样NSYRLConnection就不会被马上释放,而是会等到RunLoop执行完毕后才会释放
- 如果是通过alloc/init+startImmediately:方法调用,则不要要自己创建NSRunLoop对象,只需调用NSURLConnection的start方法,那么系统会将NSURLConnection添加到当前线程runloop的默认模式下,如果当前线程的runloop不存在, 那么系统内部会自动创建一个
- 在子线程中发送请求
-(void)sendRequestInSubThread{ dispatch_async(dispatch_get_global_queue(0, 0), ^{ // 1.创建请求 NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; // alloc/init方法会自动发送请求,默认发送异步请求(默认是在主线程中执行) // 2.发送请求 NSRunLoop *runloop = [NSRunLoop currentRunLoop]; NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self]; [conn setDelegateQueue:[[NSOperationQueue alloc] init]]; [runloop run]; // 如果是在子线程中发送请求, 那么子线程默认没有RunLoop, 所以NSURLConnection会被释放 // NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; // 设置代理方法回调的线程(设置在子线程中调用) // [conn setDelegateQueue:[[NSOperationQueue alloc] init]]; // 如果调用NSURLConnection的start方法, 那么系统会将NSURLConnection添加到当前线程runloop的默认模式下, 如果当前线程的runloop不存在, 那么系统内部会自动创建一个 // [conn start]; });}
0 0
- 网络篇 - 03.NSURLConnection应用场景
- 网络篇 - 07.NSURLSession的应用场景
- 网络篇 - 02.NSURLConnection基本使用
- NSURLConnection 网络超时
- [转载]网络开发 NSURLConnection
- 网络请求NSURLConnection
- 15-网络-NSURLConnection
- iOS 网络之NSURLConnection
- iOS网络请求NSURLConnection
- 网络:NSURLConnection队列
- 网络:NSURLConnection 缓存
- 网络请求(NSURLConnection)
- 网络:NSURLConnection 使用 HTTPS
- iOS网络请求-NSURLConnection
- iOS网络编程--NSURLConnection
- 网络请求之-NSURLConnection
- NSUrlConnection 网络下载
- iOS之网络—— Runloop、Runloop应用、网络基础、NSURLConnection
- 网络篇 - 05.网络数据解析(XML)
- 核心栈
- Android Animation动画实战(一): 从布局动画引入ListView滑动时,每一Item项的显示动画
- Unity3D 调用Android原生方法
- Dotcms中文教程之一:管理(1.1、数据库配置)
- 网络篇 - 03.NSURLConnection应用场景
- Java设计模式---工厂模式
- C/C++笔记(二) 2015/8/15
- match_parent和fill_parent的区别
- android连接wifi相关知识
- 字符串及其操作函数
- zoj3471 Most Powerful 状压dp
- 最新版SDWebImage的使用
- hdoj 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活【背包问题】