你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS網絡編程(六) NSURLSession詳解

iOS網絡編程(六) NSURLSession詳解

編輯:IOS開發綜合
昨夜浏覽Demo的時候,看到別人請求網絡數據用的是NSURLSession,當時就在想這裡什麼,怎麼沒有用過,引起了我的好奇心,遂去百度-谷歌-官方文檔一一查看,有了一定的了解,原來NSURLSession是iOS7中新的網絡接口,它與咱們熟悉的NSURLConnection是並列的。 查找資料,寫了一個小Demo,大家可以看看,有什麼不足的地方,可以留言幫我指出來.
//
//  HMTRootViewController.m
//  
//
//  Created by HMT on 14-6-7.
//  Copyright (c) 2014年 胡明濤. All rights reserved.
//

#import "HMTRootViewController.h"
#import "HMTAppDelegate.h"


@interface HMTRootViewController ()

@property (nonatomic,strong)UIImageView *imageView;
@property (nonatomic,strong)UIProgressView *progressIndicator;

@property (nonatomic,strong)NSURLSession *urlSession;         //  普通會話
//@property (nonatomic,strong)NSURLSession *backgroundSession;  //  後台會話
@property (nonatomic,strong)NSURLSessionDownloadTask *sessionDownloadTask;  //  下載Task
@property (nonatomic,strong)NSURLSessionDownloadTask *resumableTask;        //  恢復下載Task
@property (nonatomic,strong)NSURLSessionDownloadTask *backgroundTask;       //  後台下載Task
@property (nonatomic,strong)NSData *partialData;   //  下載的局部數據

@end

@implementation HMTRootViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad{

    [super viewDidLoad];
    
    self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 64, 320, 300)];
    [self.view addSubview:_imageView];
    
    self.progressIndicator = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
    _progressIndicator.frame = CGRectMake(50, 500, 220, 50);
    [self.view addSubview:_progressIndicator];
    
    UIButton *cancleButton = [UIButton buttonWithType:UIButtonTypeSystem];
    cancleButton.frame = CGRectMake(120, 400, 40, 40);
    [cancleButton setTitle:@"取消" forState:UIControlStateNormal];
    [cancleButton addTarget:self action:@selector(didClickCancleButtonAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:cancleButton];
    
    UIButton *downloadButton = [UIButton buttonWithType:UIButtonTypeSystem];
    downloadButton.frame = CGRectMake(20, 400, 40, 40);
    [downloadButton setTitle:@"下載" forState:UIControlStateNormal];
    [downloadButton addTarget:self action:@selector(didClickDownloadButtonAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:downloadButton];
    
    UIButton *uploadButton = [UIButton buttonWithType:UIButtonTypeSystem];
    uploadButton.frame = CGRectMake(70, 400, 40, 40);
    [uploadButton setTitle:@"上傳" forState:UIControlStateNormal];
    [uploadButton addTarget:self action:@selector(didClickUploadButtonAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:uploadButton];
    
    UIButton *resumableButton = [UIButton buttonWithType:UIButtonTypeSystem];
    resumableButton.frame = CGRectMake(180, 400, 40, 40);
    [resumableButton setTitle:@"恢復" forState:UIControlStateNormal];
    [resumableButton addTarget:self action:@selector(didClickResuableButtonAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:resumableButton];
    
    UIButton *backgroundLoadButton = [UIButton buttonWithType:UIButtonTypeSystem];
    backgroundLoadButton.frame = CGRectMake(220, 400, 80, 40);
    [backgroundLoadButton setTitle:@"後台下載" forState:UIControlStateNormal];
    [backgroundLoadButton addTarget:self action:@selector(didClickBackgroundButtonAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:backgroundLoadButton];
    

    #pragma mark - 如果我們需要利用NSURLSession進行數據傳輸我們需要:
    /**
     *  創建一個NSURLSessionConfiguration,用於創建NSSession時設置工作模式(3種)
     *  (1)一般模式(default):工作模式類似於原來的NSURLConnection,可以使用緩存的Cache,Cookie,鑒權。
     *  (2)及時模式(ephemeral):不使用緩存的Cache,Cookie,鑒權。
     *  (3)後台模式(background):在後台完成上傳下載,創建Configuration對象的時候需要給一個NSString的ID用於追蹤完成工作的Session是哪一個
     */
    NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
    
    /**
     *  @網絡設置:參考NSURLConnection中的設置項
     *  兩種創建方法(目前不太懂什麼區別)
     *  (1)就是根據剛才創建的Configuration創建一個Session,系統默認創建一個新的OperationQueue處理Session的消息
     *  (2)可以設定回調的delegate(注意這個回調delegate會被強引用),並且可以設定delegate在哪個OperationQueue回調,如果我們將其
     *     設置為[NSOperationQueue mainQueue]就能在主線程進行回調非常的方便
     */
    //NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
    self.urlSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    NSURL *url = [NSURL URLWithString:@"http://www.bizhiwa.com/uploads/allimg/2012-01/22021207-1-311536.jpg"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    /**
     *  NSURLSessionUploadTask:上傳用的Task,傳完以後不會再下載返回結果;
     *  NSURLSessionDownloadTask:下載用的Task;
     *  NSURLSessionDataTask:可以上傳內容,上傳完成後再進行下載。
     */
    self.sessionDownloadTask = [self.urlSession downloadTaskWithRequest:request];

    //  同NSURLConnection一樣,有代理方法也就有block方法
//    [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
//    }];
    
}

#pragma mark 點擊下載
- (void)didClickDownloadButtonAction:(UIButton *)button{

    // 因為任務默認是掛起狀態,需要恢復任務(執行任務)
    [_sessionDownloadTask resume];

}

#pragma mark 點擊上傳
- (void)didClickUploadButtonAction:(UIButton *)button{

    //判斷imageView是否有內容
    if (_imageView.image == nil) {
        
        NSLog(@"image view is empty");
        return;
        
    }
    
    // 0. 上傳之前在界面上添加指示符
    UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    // 設置位置???
    CGSize size = _imageView.bounds.size;
    indicator.center = CGPointMake(size.width / 2.0, size.height / 2.0);
    [self.imageView addSubview:indicator];
    [indicator startAnimating];
    
    // 1. URL
    NSURL *url = [NSURL URLWithString:@"http://www.bizhiwa.com/uploads/allimg/2012-01/22021207-1-311536.jpg"];
    
    // 2. Request -> PUT,request的默認操作是GET
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0f];
    request.HTTPMethod = @"PUT";
    
    // *** 設置網絡請求的身份驗證! ***
    
    // 1> 授權字符串
    
    NSString *authStr = @"admin:123456";
    
    // 2> BASE64的編碼,避免數據在網絡上以明文傳輸
    // iOS中,僅對NSData類型的數據提供了BASE64的編碼支持
    NSData *authData = [authStr dataUsingEncoding:NSUTF8StringEncoding];
    NSString *encodeStr = [authData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn];
    NSString *authValue = [NSString stringWithFormat:@"Basic %@", encodeStr];
    [request setValue:authValue forHTTPHeaderField:@"Authorization"];
    
    // 3. Session
    NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    
    // 4. UploadTask
    NSData *imageData = UIImageJPEGRepresentation(_imageView.image, 0.75);
    //  應用block的請求方式
    NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:imageData completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        
        // 上傳完成後,data參數轉換成string就是服務器返回的內容
        
        NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"OK -> %@", str);

        [NSThread sleepForTimeInterval:5.0f];
        
        dispatch_async(dispatch_get_main_queue(), ^{
            
            [indicator stopAnimating];
            [indicator removeFromSuperview];
            
        });
        
    }];
    
    // 因為任務默認是掛起狀態,需要恢復任務(執行任務)
    [uploadTask resume];
}

#pragma mark 點擊取消
//  NSURLConnection一旦發送是沒法取消的。但是,我們可以很容易的取消掉一個NSURLSessionTask任務
- (void)didClickCancleButtonAction:(UIButton *)button{

    /**
     *  當取消後,會回調這個URLSession:task:didCompleteWithError:代理方法,通知你去及時更新UI。當取消一個任務後,也
     *  十分可能會再一次回調這個代理方法URLSession:downloadTask:didWriteData:BytesWritten:totalBytesExpectedToWrite:
     *  當然,didComplete 方法肯定是最後一個回調的。
     */
//    if (_sessionDownloadTask) {
//        
//        //  取消下載請求
//        [_sessionDownloadTask cancel];
//        _sessionDownloadTask = nil;
//    }

    if (!self.sessionDownloadTask) {
        
        //  停止下載任務,把待恢復的數據保存到一個變量中,方便後面恢復下載使用
        [self.sessionDownloadTask cancelByProducingResumeData:^(NSData *resumeData) {
           
            self.partialData = resumeData;
            self.sessionDownloadTask = nil;
            
        }];
    }
}

#pragma mark  恢復下載(斷點續傳)
- (void)didClickResuableButtonAction:(UIButton *)button{
    
        if (self.partialData) {
            
            self.sessionDownloadTask = [self.urlSession downloadTaskWithResumeData:self.partialData];
            self.partialData = nil;
            
        }else{
        
            NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://pic4.duowan.com/wow/1002/130782267821/130782458426.jpg"]];
            self.resumableTask = [self.urlSession downloadTaskWithRequest:request];
        
        }
        
        [self.sessionDownloadTask resume];

}

#pragma mark 後台下載模式
- (void)didClickBackgroundButtonAction:(UIButton *)button{
    
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://dldir1.qq.com/qqfile/QQforMac/QQ_V3.1.2.dmg"]];
    self.backgroundTask = [[self backgroundSession] downloadTaskWithRequest:request];
    
    [self.backgroundTask resume];
}

#pragma mark - NSURLSessionDownloadTaskDelegate
//  下載完成
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{

    /**
      *******->appDelegete裡面的方法
     typedef void(^MyBlock)();
     @property (copy, nonatomic) MyBlock backgroundURLSessionCompletionHandler;
     //  後台請求結束時調用的方法
     - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler{
     
     self.backgroundURLSessionCompletionHandler = completionHandler;
     }
     
     */
    //  如果是後台NSURLSession,後台請求結束後會調用這個方法,通知你應該更新UI了
    if (session == [self backgroundSession]) {
        
        self.backgroundTask = nil;
        HMTAppDelegate *appDelegate = (HMTAppDelegate *)[UIApplication sharedApplication].delegate;
        if (appDelegate.backgroundURLSessionCompletionHandler) {
            
            void(^handler)() = appDelegate.backgroundURLSessionCompletionHandler;
            appDelegate.backgroundURLSessionCompletionHandler = nil;
            handler();
        }
        
    }
    
    //  這裡的緩存處理做的不好,大家按自己的方法處理就行,還有圖片的存儲以它本身的URL路徑為准,這樣是不會有重復的
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSURL *cachesURLPath = [[fileManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject];
    //  根據URL獲取到下載的文件名,拼接成沙盒的存儲路徑(location是下載的臨時文件目錄,在tmp文件夾裡面)
    NSURL *destinationPath = [cachesURLPath URLByAppendingPathComponent:[location lastPathComponent]];
    
    NSError *error = nil;
    BOOL success = [fileManager moveItemAtURL:location toURL:destinationPath error:&error];
    [fileManager removeItemAtURL:location error:NULL];
    
    //  location是下載的臨時文件目錄,將文件從臨時文件夾復制到沙盒
//    BOOL success = [fileManager copyItemAtURL:location toURL:destinationPath error:&error];
    if (success) {
        
        dispatch_async(dispatch_get_main_queue(), ^{
           
            UIImage *image = [UIImage imageWithContentsOfFile:[destinationPath path]];
            self.imageView.image = image;
            //  UIImageView會自動裁剪圖片適應它的frame,下面這個屬性就是展示原圖
            self.imageView.contentMode = UIViewContentModeScaleAspectFill;
        
        });
    }

}

//  不管任務是否成功,在完成後都會回調這個代理方法
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    
    //  如果error是nil,則證明下載是成功的,否則就要通過它來查詢失敗的原因。如果下載了一部分,這個error會包含一個NSData對象,如果後面要恢復任務可以用到
    if (error == nil) {
        
        dispatch_async(dispatch_get_main_queue(), ^{
            
            self.progressIndicator.hidden = YES;
        });
    }

}

//  傳輸進度
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{

    double currentValue = totalBytesWritten / (double)totalBytesExpectedToWrite;
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"%f",currentValue);
        self.progressIndicator.hidden = NO;
        self.progressIndicator.progress = currentValue;
    });
}

//  未知
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{
    
}

#pragma mark - NSURLSession另一個重要的特性:即使當應用不在前台時,你也可以繼續傳輸任務。當然,我們的會話模式也要為後台模式
- (NSURLSession *)backgroundSession{

    //  通過給的後台token,我們只能創建一個後台會話,所以這裡使用dispatch once block
    static NSURLSession *backgroundSession = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.shinobicontrols.BackgroundDownload.BackgroundSession"];
        backgroundSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
        
    });
    
    return backgroundSession;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end

@不錯的NSURLSession文章
iOS 7系列譯文:忘記NSURLConnection,擁抱NSURLSession吧!



  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved