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];
});