HTTP请求的方案
- NSURLConnection:用法简单,(坑比较多)
- NSURLSession:功能比NSURLConnection强大,苹果目前比较推荐
- CFNetwork :苹果底层,纯C语言
- ASIHTTPRequest:功能强大,已停止更新
- AFNetworking:简单易用,三方库中比较主流
NSURLConnection
- 基本类
- NSURL:请求地址
- NSURLRequest:代表一个请求,它包含(NSURL对象、请求方法、请求头、请求体、请求超时时间…)
- NSMutableURLRequest:是NSURLRequest子类,可修改Request里的信息
- NSURLConnection:负责发送请求,建立客户端和服务器的连接,发送数据并收集来自服务器响应的数据
- 接收大数据一般使用代理方法,小数据使用块方法即可
使用步骤
- 创建一个NSURL对象,设置请求路径
- 传入NSURL创建一个NSURLRequest对象,设置请求头和请求体
- 使用NSURLConnection发送请求
请求方式
- 同步请求(当前线程)
- 异步请求(开启新线程)
同步Get请求
- (IBAction)syncGet:(id)sender {
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/ping"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSString *requestInfo = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
self.textView.text = requestInfo;
NSLog(@"syncGetData:%@--%@",requestInfo,[NSThread currentThread]);
}
- 同步Post请求
- (IBAction)syncPost:(id)sender {
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/upload"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//更改请求方法
request.HTTPMethod = @"POST";
//设置超时5s
request.timeoutInterval = 5;
request.HTTPBody = [@"ss" dataUsingEncoding:NSUTF8StringEncoding];
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSString *requestInfo = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"syncPostData:%@--%@",requestInfo,[NSThread currentThread]);
}
- 异步Get请求
- (IBAction)asynGet:(id)sender {
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/ping"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//queue:可以设置指定的线程,如主线程[NSOperationQueue mainQueue]
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc]init] completionHandler:^(NSURLResponse * response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSString *requestInfo = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"asynGetData:%@--%@",requestInfo,[NSThread currentThread]);
}];
}
- 异步Post请求
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/upload"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//更改请求方法
request.HTTPMethod = @"POST";
//设置超时5s
request.timeoutInterval = 5;
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSString *requestInfo = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"asynPostData:%@--%@",requestInfo,[NSThread currentThread]);
}];
- 小文件下载
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/download"];
//方式一:data直接下载
NSData *data = [NSData dataWithContentsOfURL:url];
NSLog(@"\n NSData---%lu",(unsigned long)data.length);
self.textView.text = [NSString stringWithFormat:@"下载完成:%lu",(unsigned long)data.length];
//方式二:通过请求下载
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSLog(@"\n NSURLConnection---%lu",(unsigned long)data.length);
self.textView.text = [NSString stringWithFormat:@"下载完成:%lu",(unsigned long)data.length];
}];
大文件下载
- 一般下载方式
1、收到服务器响应后初始化一个要存放data的临时缓存区
2、将不断接收的data存放临时缓存区里
3、接收完毕后将临时缓存里的data放入本地存储文件
4、存储完毕后,将临时缓存区清空
弊端:正整个下载过程中,app内存会不断增长,直到将数据写入才会降下来才会降下来
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/download"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [NSURLConnection connectionWithRequest:request delegate:self]; --------- -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response{ //获取文件总长度 self.contentLength = [response.allHeaderFields[@"Content-Length"] integerValue]; self.fileData = [NSMutableData data]; } -(void)connection:(NSURLConnection *)connection didReceiveData:(nonnull NSData *)data{ [self.fileData appendData:data]; NSLog(@"下载进度--%.2f%%",(1.0 * self.fileData.length / self.contentLength) * 100); self.textView.text = [NSString stringWithFormat:@"下载进度--%.2f%%",(1.0 * self.fileData.length / self.contentLength) * 100]; } -(void)connectionDidFinishLoading:(NSURLConnection *)connection{ //将临时缓存去的data写入本地文件 [self.fileData writeToFile:SaveFile atomically:YES]; self.fileData = nil; NSLog(@"写入完毕--%@",SaveFile); self.textView.text = [NSString stringWithFormat:@"%@ \n写入完毕--%@",self.textView.text,SaveFile]; }
优化下载过程
为了避免app内存不断增长,最好处理方法是边下载边存储
1、收到服务器响应后,创建一个本地存储的空文件
2、将不断接收的data存放临时缓存区里
3、接收完毕后将临时缓存里的data放入本地存储
4、存储完毕后,将临时缓存区清空NSURL *url = [NSURL URLWithString:@"http://localhost:3000/download"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [NSURLConnection connectionWithRequest:request delegate:self]; --------- -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response{ //获取文件总长度 self.contentLength = [response.allHeaderFields[@"Content-Length"] integerValue]; //新建一个空文件 [[NSFileManager defaultManager]createFileAtPath:SaveFile contents:nil attributes:nil]; //接收得到data,直接把data写入创建好的文件 self.handle = [NSFileHandle fileHandleForWritingAtPath:SaveFile]; } -(void)connection:(NSURLConnection *)connection didReceiveData:(nonnull NSData *)data{ //指定每一次data写入位置(是指针指向目前存储数据的最后面,避免上一次收到的data覆盖目前的data) [self.handle seekToEndOfFile]; //写入数据 [self.handle writeData:data]; self.currentLength += data.length; NSLog(@"下载进度--%.2f%%",(1.0 * self.currentLength / self.contentLength) * 100); self.textView.text = [NSString stringWithFormat:@"下载进度--%.2f%%",(1.0 * self.currentLength / self.contentLength) * 100]; } -(void)connectionDidFinishLoading:(NSURLConnection *)connection{ //存储完毕 //关闭handle [self.handle closeFile]; self.textView.text = [NSString stringWithFormat:@"%@ \n写入完毕--%@",self.textView.text,SaveFile]; }
通过stream下载
以数据流的形式进行存储
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/download"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [NSURLConnection connectionWithRequest:request delegate:self]; ------ -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response{ //利用NSOutputStream往path中写data(append为YES,每次写入都是追加到文件尾部) self.stream = [[NSOutputStream alloc]initToFileAtPath:SaveFile append:YES]; // [self.stream open]; } -(void)connection:(NSURLConnection *)connection didReceiveData:(nonnull NSData *)data{ [self.stream write:[data bytes] maxLength:data.length]; } -(void)connectionDidFinishLoading:(NSURLConnection *)connection{ //关闭stream [self.stream close]; NSLog(@"写入完毕--%@",SaveFile); self.textView.text = [NSString stringWithFormat:@"%@ \n写入完毕--%@",self.textView.text,SaveFile]; }
- 一般下载方式
创建压缩文件
pod SSZipArchive库
NSArray *paths = @[
@"/Users/soso/Desktop/OTP2.png",
@"/Users/soso/Desktop/OTP1.png"
];
[SSZipArchive createZipFileAtPath:@"/Users/soso/Desktop/test.zip" withFilesAtPaths:paths];
}
- 解压文件
[SSZipArchive unzipFileAtPath:@"/Users/soso/Desktop/test.zip" toDestination:@"/Users/soso/Desktop"];
- NSURLConnection与Runloop的关系
虽然发送请求写在主线程里,其实内部在是子线程进行的,但是所以的回调方法都是在主线程返回,如果想让回调方法在子线程中进行,那么通过设置队列的方式去其代理回调在其子线程执行
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/download"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
//决定代理方法在哪个队列执行
[connection setDelegateQueue:[[NSOperationQueue alloc]init]];
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response{
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(nonnull NSData *)data{
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
}
如果将其请求放在子线程中调用,会导致一个问题,代理的回调方法将无响应,其内部应该当请求发出去时,代理方法将等待服务器的回调,内部应该有一个runloop一直在监听代理回调,一旦runloop监听到source,就去执行处理,为什么在子线程调用就不好使,是因为子线程中runloop默认是没有启动的,因此需要手动启动runloop
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/download"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
//启动子线程runloop
[[NSRunLoop currentRunLoop] run];
});