iOS中的網絡編程-NSURLConnection簡單使用(二)

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

});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章