NSURLSession与AFNetworking3.0

下面是用GET方式请求一个页面数据的示例:

AFNetworking 2.x

NSString *siteUrl = @"http://webinar.ofweek.com/readDemoFile.action";
NSDictionary *parameters = @{@"activity.id":@"9866033",@"user.id":@"2"};

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html"];
//    manager.responseSerializer.acceptableContentTypes = [manager.responseSerializer.acceptableContentTypes setByAddingObject:@"text/html; charset=GBK"];

[manager GET:siteUrl parameters:parameters
     success:^(AFHTTPRequestOperation * _Nonnull operation, id  _Nonnull responseObject) {
         NSLog(@"responseString:%@",operation.responseString);
         
         NSData *responseData = operation.responseData;
         NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
         NSString *strResponseData = [[NSString alloc] initWithData:responseData encoding:enc];
         NSLog(@"responseData:%@",strResponseData);
         
         NSLog(@"responseObject:%@",responseObject);
     }
     failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) {
         NSLog(@"failed,%@",error);
     }
];
Code

AFNetworking 3.x

NSString *siteUrl = @"https://www.shopbop.com/actions/viewSearchResultsAction.action";
NSDictionary *parameters = @{@"query":@"bag",@"baseIndex":@"80"};

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

manager.responseSerializer = [AFHTTPResponseSerializer serializer];

[manager GET:siteUrl parameters:parameters
     success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"success,%@",task.response.MIMEType);

        NSString *responseString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
        NSLog(@"%@", responseString);
    }
     failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
    NSLog(@"failed");
    }
 ];
Code

iOS9中引入了新特性App Transport Security (ATS),该特性要求App内访问网站必须使用HTTPS协议。下面的方法简单的关闭了这个限制:

1. Info.plist中添加新项NSAppTransportSecurity类型为Dictionary

2. NSAppTransportSecurity下添加子项NSAllowsArbitraryLoads,类型为Boolean,值设为YES

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

NSURLSession

NSURLSession是iOS7新推出的网络接口,它与以前的NSURLConnection是并列的。如果用户强制关闭应用,NSURLSesssion会断掉。它支持三种类型的任务:加载数据,下载和上传。

利用NSURLSession进行数据传输需要以下流程:

创建一个NSURLSessionConfiguration,用于接下来创建NSSession时需要的工作模式

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];

工作模式分为:

默认会话模式(default) — 工作模式类似于原来的NSURLConnection,使用的是基于磁盘缓存的持久化策略,使用用户keychain中保存的证书进行认证授权。

瞬时会话模式(ephemeral) — 该模式不适用磁盘保存任何数据。所有和会话相关的caches,证书,cookies等都被保存在RAM中,因此当程序使会话无效,这些缓存的数据就会被自动清空。

后台会话模式(background) — 该模式在后台完成上传和下载,在创建configuration对象的时候需要提供一个NSString类型的ID用于标识完成工作的后台会话。

NSURLConnection实际上是由一系列组件组成,包括有:NSURLRequest、NSURLResponse、NSURLProtocol、NSURLCache、NSHTTPCookieStorage、NSURLCredentialStorage以及同名的NSURLConnection。

在WWDC2013中,Apple的开发团队对NSURLConnection进行了重构,并推出了NSURLSession作为替代。NSURLSession的大部分组件与NSURLConnection中的组件相同,不同在处在于它将NSURLConnection替换为NSURLSession和NSURLSessionConfiguration,以及三个NSURLSessionTask的子类:NSURLSessionDataTask、NSURLSessionUploadTask和NSURLSessionDownloadTask。

下面是NSURLSession新推出的类:

NSURLSessionConfiguration类

可以通过该类配置会话的工作模式,三种模式的代码如下:

+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
+ (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier;

在backgroundSessionConfiguration:方法中的identifier参数指定了会话ID,用于标记后台的session。

该类还有两个重要的属性:

/* allow request to route over cellular. */
@property BOOL allowsCellularAccess;

/* allows background tasks to be scheduled at the discretion of the system for optimal performance. */
@property (getter=isDiscretionary) BOOL discretionary NS_AVAILABLE(NA, 7_0);

allowsCellularAccess属性指定是否允许使用蜂窝连接,discretionary属性为YES时表示当程序在后台运作时由系统自己选择最佳的网络连接配置,在使用后台传输数据的时候,建议使用discretionary属性而不是allowsCellularAccess属性,因为discretionary属性会综合考虑WiFi可用性和电池电量。(当设备有足够电量时,设备才通过WiFi进行数据传输。如果电量低,或者只打开了蜂窝连接,传输任务将不会执行)

NSURLSession类

获取该类的实例有下面几种方式:

/*
 * The shared session uses the currently set global NSURLCache,
 * NSHTTPCookieStorage and NSURLCredentialStorage objects.
 */
+ (NSURLSession *)sharedSession;

/*
 * Customization of NSURLSession occurs during creation of a new session.
 * If you only need to use the convenience routines with custom
 * configuration options it is not necessary to specify a delegate.
 * If you do specify a delegate, the delegate will be retained until after
 * the delegate has been sent the URLSession:didBecomeInvalidWithError: message.
 */
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id <NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue;

第一种方式是使用静态的sharedSession方法,将返回共享的会话,该会话使用全局的Cache、Cookie和证书。

第二种方式是通过sessionWithConfiguration:方法根据NSURLSessionConfiguration的值创建对应工作模式下的会话。

第三种方式是通过sessionWithConfiguration:delegate:delegateQueue方法创建对象,在第二种方式的基础上增加了session的委托和委托所处的队列。当不再需要连接时,可以调用NSURLSession的invalidateAndCancel方法直接关闭,或者调用finishTasksAndInvalidate等待当前Task结束后关闭。这两种关闭方法都会触发delegate类的URLSession:didBecomeInvalidWithError:事件。

NSURLSessionTask类

这是一个抽象类,它包含三个子类:NSURLSessionDataTask,NSURLSessionUploadTask和NSURLSessionDownloadTask。这三个类封装了获取数据,上传和下载任务。下面是继承关系图:

(1)NSURLSessionDataTask

利用NSURLRequest对象或NSURL对象来创建该类的实例:

/* Creates a data task with the given request.  The request may have a body stream. */
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;

/* Creates a data task to retrieve the contents of the given URL. */
- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;

利用NSURLRequest对象或NSURL对象来创建该类的实例,当任务完成后,通过completionHandler指定回调的代码块:

/*
 * data task convenience methods.  These methods create tasks that
 * bypass the normal delegate calls for response and data delivery,
 * and provide a simple cancelable asynchronous interface to receiving
 * data.  Errors will be returned in the NSURLErrorDomain,
 * see <Foundation/NSURLError.h>.  The delegate, if any, will still be
 * called for authentication challenges.
 */
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;

读取数据示例:

#import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *progressCircle;
@property (weak, nonatomic) IBOutlet UIWebView *webView;
- (IBAction)loadButtonClicked:(id)sender;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

}

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

- (IBAction)loadButtonClicked:(id)sender {
    // 开始加载数据,开始转动
    [self.progressCircle startAnimating];
    
    NSURL *url = [NSURL URLWithString:@"http://tech.qq.com/it.htm"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                                completionHandler:
                                      ^(NSData *data, NSURLResponse *response, NSError *error) {
                                          //打印出返回的状态码,请求成功返回200
                                          NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
                                          NSInteger responseStatusCode = [httpResponse statusCode];
                                          NSLog(@"%ld", (long)responseStatusCode);
                                          
                                          //打印出返回的代码
                                          NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
                                          NSString *strResponseData = [[NSString alloc] initWithData:data encoding:enc];
                                          NSLog(@"ResponseData String:%@",strResponseData);
                                          
                                         

                                          // 在UIWebView中加载数据
                                          [self.webView loadData:data
                                                        MIMEType:@"text/html"
                                                textEncodingName :@"utf-8"
                                                         baseURL:[NSURL URLWithString:@"http://tech.qq.com"]];
                                          
                                          //加载数据完毕,停止转动
                                          if ([NSThread isMainThread])
                                          {
                                              [self.progressCircle stopAnimating];
                                          }
                                          else
                                          {
                                              dispatch_sync(dispatch_get_main_queue(), ^{
                                                  [self.progressCircle stopAnimating];
                                              });
                                          }
                                      }];
    //使用resume方法启动任务
    [dataTask resume];
}
@end
Code

(2)NSURLSessionUploadTask

利用NSURLRequest对象创建该类的实例,在上传时指定文件源或数据源

/* Creates an upload task with the given request.  The body of the request will be created from the file referenced by fileURL */
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;

/* Creates an upload task with the given request.  The body of the request is provided from the bodyData. */
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;

/* Creates an upload task with the given request.  The previously set body stream of the request (if any) is ignored and the URLSession:task:needNewBodyStream: delegate will be called when the body payload is required. */
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;

同上,当任务完成后,通过completionHandler指定回调的代码块:

/*
 * upload convenience method.
 */
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;

上传示例:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <NSURLSessionDataDelegate>
@end

#import "ViewController.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

}

- (IBAction)testClicked:(id)sender {
    NSURL *uploadURL = [NSURL URLWithString:@"http://www.synchemical.com/temp/UploadImage4.ashx?param2=iphone6"];
    NSString *boundary = @"----------V2ymHFg03ehbqgZCaKO6jy";
    
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
    
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *uploadFilePath = [documentsDirectory stringByAppendingPathComponent:@"ad6.png"];
    NSLog(@"file path: %@",uploadFilePath);
    NSString *fileName = [uploadFilePath lastPathComponent];
    
    NSMutableData *mutableData = [NSMutableData data];
    NSData *fileData = [[NSData alloc] initWithContentsOfFile:uploadFilePath];
//    NSData *dataOfFile = UIImageJPEGRepresentation(self.imageView.image,1.0);
    
    if (fileData) {
        //Body part for "textContent" parameter. This is a string.
        NSString *textContent = @"This is a parameter string";
        [mutableData appendData:[[NSString stringWithFormat:@"--%@
", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [mutableData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name="%@"

", @"textContent"] dataUsingEncoding:NSUTF8StringEncoding]];
        [mutableData appendData:[[NSString stringWithFormat:@"%@
", textContent] dataUsingEncoding:NSUTF8StringEncoding]];
        //end
        
        
        [mutableData appendData:[[NSString stringWithFormat:@"--%@
", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [mutableData appendData:[[NSString stringWithFormat:@"Content-Disposition:form-data; name="myfile"; filename="%@"
",fileName] dataUsingEncoding:NSUTF8StringEncoding]];
        [mutableData appendData:[@"Content-Type:application/zip

" dataUsingEncoding:NSUTF8StringEncoding]];
        [mutableData appendData:fileData];
        [mutableData appendData:[[NSString stringWithFormat:@"
"] dataUsingEncoding:NSUTF8StringEncoding]];
        [mutableData appendData:[[NSString stringWithFormat:@"--%@--
", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    }

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:uploadURL];
    [request setHTTPMethod:@"POST"];
    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
    [request setValue:contentType forHTTPHeaderField:@"Content-Type"];
    
    NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:mutableData completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSString *message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"message:%@", message);
        
        [session invalidateAndCancel]; //当不再需要连接时,可以调用Session的invalidateAndCancel直接关闭
    }];
    
    [uploadTask resume];
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
    NSLog(@"already sent:%lld",bytesSent);
    NSLog(@"totoal to send:%lld",totalBytesSent);
    NSLog(@"expected send:%lld",totalBytesExpectedToSend);
}

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

@end

<%@ WebHandler Language="C#" Class="UploadImage" %>
using System;
using System.Web;
using System.IO;
public class UploadImage : IHttpHandler
{
    public void ProcessRequest(HttpContext context) {         
        HttpPostedFile myFile = context.Request.Files["myfile"];
        string strFolder = HttpContext.Current.Server.MapPath(context.Request["folder"]);
        myFile.SaveAs(strFolder +"/uploadFolder/" + myFile.FileName);
        if (!Directory.Exists(strFolder)) 
        {
            Directory.CreateDirectory(strFolder);
        }

        context.Response.Write("param: "+context.Request["textContent"]);
   }
    
    public bool IsReusable {
        get {
            return false;
        }
    }
}
Code

(3)NSURLSessionDownloadTask

利用NSURLRequest对象或NSURL对象来创建该类的实例:

/* Creates a download task with the given request. */
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;

/* Creates a download task to download the contents of the given URL. */
- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url;

/* Creates a download task with the resume data.  If the download cannot be successfully resumed, URLSession:task:didCompleteWithError: will be called. */
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;

利用NSURLRequest对象或NSURL对象来创建该类的实例,当任务完成后,通过completionHandler指定回调的代码块,最后一个方法支持通过之前未下载完成的数据继续下载

/*
 * download task convenience methods.  When a download successfully
 * completes, the NSURL will point to a file that must be read or
 * copied during the invocation of the completion routine.  The file
 * will be removed automatically.
 */
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;
- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;

下载示例:

#import "ViewController.h"

@interface ViewController () <NSURLSessionDelegate> {
    NSData *partialData;
}
@property (strong, nonatomic) NSURLSessionDownloadTask *downloadTask;
@property (strong, nonatomic) NSURLSession *session;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *progressCircle;
@property (weak, nonatomic) IBOutlet UIWebView *webView;
- (IBAction)downloadButtonClicked:(id)sender;

@end

@implementation ViewController
- (IBAction)downloadButtonClicked:(id)sender {
    [self.progressCircle startAnimating];
    
    NSURL *URL = [NSURL URLWithString:@"http://www.synchemical.com/temp/2800kb.jpg"];
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]];

    self.downloadTask = [self.session downloadTaskWithRequest:request];
    [self.downloadTask resume];
}

- (IBAction)resumeButtonClicked:(id)sender {
    // 传入上次暂停下载返回的数据,就可以恢复下载
    self.downloadTask = [self.session downloadTaskWithResumeData:partialData];
    
    // 开始任务
    [self.downloadTask resume];
    
    // 清空
    partialData = nil;
}

- (IBAction)pauseButtonClicked:(id)sender {
    [self.downloadTask cancelByProducingResumeData:^(NSData *resumeData) {
        partialData = resumeData;
        self.downloadTask = nil;
    }];
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
    NSLog(@"=========下载进度=========");
    // 获得下载进度
    NSLog(@"totalBytesWritten:%lld",totalBytesWritten);
    NSLog(@"totalBytesExpectedToWrite:%lld",totalBytesExpectedToWrite);
    NSLog(@"download percent:%f",(double)totalBytesWritten / totalBytesExpectedToWrite);
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
    NSLog(@"finish download");
    //打印出返回的状态码,请求成功返回200
    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)downloadTask.response;
    NSInteger responseStatusCode = [httpResponse statusCode];
    NSLog(@"hxw: %ld", (long)responseStatusCode);
    
    //输出下载文件原来的存放目录
    NSLog(@"file tmp path: %@", location);
    //设置文件的存放目录
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsPath = [paths objectAtIndex:0];
    NSURL *documentsURL = [NSURL fileURLWithPath:documentsPath];
    NSString *filePath = [[downloadTask.response URL] lastPathComponent];
    NSURL *fileURL = [documentsURL URLByAppendingPathComponent:filePath];
    NSLog(@"file save path:%@",fileURL.path);
    //如果路径下文件已经存在,先将其删除
    NSFileManager *fileManager = [NSFileManager defaultManager];
    BOOL isDir;
    if ([fileManager fileExistsAtPath:fileURL.path isDirectory:&isDir]) {
        NSLog(@"存在文件");
        if (isDir) {
            NSLog(@"它是个目录");
        }
        else {
            NSLog(@"它是个普通文件");
        }
        [fileManager removeItemAtURL:fileURL error:nil];
    }
    //移动文件
    BOOL success = [fileManager moveItemAtURL:location toURL:fileURL error:nil];
    if (success)
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            //UIImage *image = [UIImage imageWithContentsOfFile:[fileURL path]];
            //self.imageView.image = image;
            //self.imageView.contentMode = UIViewContentModeScaleAspectFill;
            //在webView中加载图片文件
            NSURLRequest *showImage_request = [NSURLRequest requestWithURL:fileURL];
            [self.webView loadRequest:showImage_request];
            
            //下载完毕,停止转动
            if ([NSThread isMainThread]) {
                [self.progressCircle stopAnimating];
            }
            else {
                dispatch_sync(dispatch_get_main_queue(), ^{
                    [self.progressCircle stopAnimating];
                });
            }
        });
    }
    else
    {
        NSLog(@"Couldn't copy the downloaded file");
    }
}


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end
Code
原文地址:https://www.cnblogs.com/CoderWayne/p/5427667.html