1、NSURLSession

  • 在 iOS9.0 之后,以前使用的 NSURLConnection 过期,苹果推荐使用 NSURLSession 来替换 NSURLConnection 完成网路请求相关操作。
  • 1.1 NSURLSession 功能

    • NSURLSession 具有断点续传,后台下载等相关功能。
    • 暂停、停止、重启网络任务,不再需要 NSOperation 封装。
    • 请求可以使用同样的配置容器中。
    • 不同的 session 可以使用不同的私有存储。
    • block 和委托可以同时起作用。
    • 可以直接从文件系统上传下载。
    • NSURLSession 的使用非常简单,先根据会话对象创建一个请求 Task,然后执行该 Task 即可。
    • NSURLSessionTask 本身是一个抽象类,在使用的时候,通常是根据具体的需求使用它的几个子类。关系如下:
    • NSURLSessionDownloadTask <-- NSURLSessionTask --> NSURLSessionDataTask --> NSURLSessionUploadTask
  • 1.2 发送 GET 请求

    • 使用 NSURLSession 发送 GET 请求的方法和 NSURLConnection 类似,整个过程如下:

      • 1)确定请求路径(一般由公司的后台开发人员以接口文档的方式提供),GET 请求参数直接跟在 URL 后面;
      • 2)创建请求对象(默认包含了请求头和请求方法【GET】),此步骤可以省略;
      • 3)创建会话对象(NSURLSession);
      • 4)根据会话对象创建请求任务(NSURLSessionDataTask);
      • 5)执行 Task;
      • 6)当得到服务器返回的响应后,解析数据(XML|JSON|HTTP)。
  • 1.3 发送 POST 请求

    • 使用 NSURLSession 发送 POST 请求的方法和 NSURLConnection 类似,整个过程如下:

      • 1)确定请求路径(一般由公司的后台开发人员以接口文档的方式提供);
      • 2)创建可变的请求对象(因为需要修改),此步骤不可以省略;
      • 3)修改请求方法为 POST;
      • 4)设置请求体,把参数转换为二进制数据并设置请求体;
      • 5)创建会话对象(NSURLSession);
      • 6)根据会话对象创建请求任务(NSURLSessionDataTask);
      • 7)执行 Task;
      • 8)当得到服务器返回的响应后,解析数据(XML|JSON|HTTP)。
  • 1.4 文件下载请求

    • 文件下载成功后,如果不做任何处理,下载的文件会被自动删除。
    • 如果显示比较大的图片,NSURLSession 可以利用磁盘缓存直接下载到本地,不会造成内存占用太大。
    • 一般从网络上下载文件,zip 压缩包会比较多。
    • 如果是 zip 文件,下载完成后需要。
      • 下载压缩包
      • 解压缩(异步执行)到目标文件夹
      • 删除压缩包
      • 下载任务的特点可以让程序员只关心解压缩的工作。
  • 1.5 文件上传请求

    • POST:

      • 需要有一个脚本做支持。
      • 有些脚本有上传文件大小限制,如 PHP 最大为 2M。
    • PUT:

      • 不需要脚本,直接以文件的方式写入服务器。
      • 如果文件不存在,就是新增,如果文件存在就是修改。
      • 文件上传需要身份验证。
      • status code: 201 新增
      • status code: 204 修改
      • status code: 401 身份验证失败

2、NSURLSession 的设置

  • 2.1 URLRequest 的设置

    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video"]];
    
    // 设置缓存策略
    /*
    // 默认的缓存策略,会在本地缓存
    NSURLRequestUseProtocolCachePolicy = 0, // 忽略本地缓存数据,永远都是从服务器获取数据,不使用缓存,应用场景:股票,彩票
    NSURLRequestReloadIgnoringLocalCacheData = 1,
    NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData // 首先使用缓存,如果没有本地缓存,才从原地址下载
    NSURLRequestReturnCacheDataElseLoad = 2, // 使用本地缓存,从不下载,如果本地没有缓存,则请求失败和 "离线" 数据访问有关,可以和 Reachability 框架结合使用,
    // 如果用户联网,直接使用默认策略。如果没有联网,可以使用返回缓存策略,郑重提示:要把用户拉到网络上来。
    NSURLRequestReturnCacheDataDontLoad = 3, // 无视任何缓存策略,无论是本地的还是远程的,总是从原地址重新下载
    NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // Unimplemented // 如果本地缓存是有效的则不下载,其他任何情况都从原地址重新下载
    NSURLRequestReloadRevalidatingCacheData = 5, // Unimplemented 缓存的数据保存在沙盒路径下 Caches 文件夹中的 SQLite 数据库中。
    */
    urlRequest.cachePolicy = NSURLRequestReturnCacheDataElseLoad; // 设置超时时间
    urlRequest.timeoutInterval = 120; // 设置请求模式
    /*
    默认是 GET
    */
    urlRequest.HTTPMethod = @"POST"; // 设置请求体
    urlRequest.HTTPBody = [@"type=JSON" dataUsingEncoding:NSUTF8StringEncoding]; // 设置请求头
    /*
    告诉服务器客户端类型,只能写英文,User-Agent 是固定的 key
    */
    [urlRequest setValue:@"iPhone 6s Plus" forHTTPHeaderField:@"User-Agent"];
  • 2.2 URLSessionConfiguration 的设置

    • 在开发一款应用程序的时候,通常只会访问一台服务器,如果所有的设置在 session 中统一设置一次,后续的网络访问方法,会非常简单,一次设置,全局有效。
    • 在 URLSession 中,会使用 config 替代很多原有 request 中的附加设置。config 用于设置全局的网络会话属性,
    • 包括:浏览器类型,Content-Type,身份验证,Cookie,超时时长,缓存策略,
    • 主机最大连接数...。NSURLSessionConfiguration 拥有 20 个属性。熟练掌握这些属性的用处,将使应用程序充分利用其网络环境。
    • 常用属性:
    HTTPAdditionalHeaders           HTTP 请求头,告诉服务器有关客户端的附加信息,这对于跨会话共享信息,
    如内容类型,语言,用户代理,身份认证,是很有用的。
    Accept 告诉服务器客户端可接收的数据类型,如:@"application/json" 。
    Accept-Language 告诉服务器客户端使用的语言类型,如:@"en" 。
    Authorization 验证身份信息,如:authString 。
    User-Agent 告诉服务器客户端类型,如:@"iPhone AppleWebKit" 。
    range 用于断点续传,如:bytes=10- 。 networkServiceType 网络服务类型,对标准的网络流量,网络电话,语音,视频,以及由一个后台进程使用的流量
    进行了区分。大多数应用程序都不需要设置这个。
    NSURLNetworkServiceTypeDefault 默认
    NSURLNetworkServiceTypeVoIP VoIP
    NSURLNetworkServiceTypeVideo 视频
    NSURLNetworkServiceTypeBackground 后台
    NSURLNetworkServiceTypeVoice 语音 allowsCellularAccess 允许蜂窝访问,和 discretionary 自行决定,被用于节省通过蜂窝连接的带宽。
    建议在使用后台传输的时候,使用 discretionary 属性,而不是 allowsCellularAccess
    属性,因为它会把 WiFi 和电源可用性考虑在内。 timeoutIntervalForRequest 超时时长,许多开发人员试图使用 timeoutInterval 去限制发送请求的总时间,但这误会了
    timeoutIntervalForRequest 的意思:报文之间的时间。
    timeoutIntervalForResource 整个资源请求时长,实际上提供了整体超时的特性,这应该只用于后台传输,而不是用户实际上
    可能想要等待的任何东西。 HTTPMaximumConnectionsPerHost 对于一个 host 的最大并发连接数,iOS 默认数值是 4,MAC 下的默认数值是 6,从某种程度上,
    替代了 NSOpeartionQueue 的最大并发线程数。是 Foundation 框架中 URL 加载系统的一个新
    的配置选项。它曾经被用于 NSURLConnection 管理私人连接池。现在有了 NSURLSession,开发
    者可以在需要时限制连接到特定主机的数量。日常开发中,几乎不用去管 session 的最大并发数。 HTTPShouldUsePipelining 也出现在 NSMutableURLRequest,它可以被用于开启 HTTP 管道,这可以显着降低请求的加载时
    间,但是由于没有被服务器广泛支持,默认是禁用的。 sessionSendsLaunchEvents 是另一个新的属性,该属性指定该会话是否应该从后台启动。 connectionProxyDictionary 指定了会话连接中的代理服务器。同样地,大多数面向消费者的应用程序都不需要代理,所以基本上不
    需要配置这个属性。关于连接代理的更多信息可以在 CFProxySupport Reference 找到。 Cookie Policies
    HTTPCookieStorage 被会话使用的 cookie 存储。默认情况下,NSHTTPCookieShorage 的 sharedHTTPCookieStorage
    会被使用,这与 NSURLConnection 是相同的。
    HTTPCookieAcceptPolicy 决定了该会话应该接受从服务器发出的 cookie 的条件。
    HTTPShouldSetCookies 指定了请求是否应该使用会话 HTTPCookieStorage 的 cookie。 Security Policies
    URLCredentialStorage 会话使用的证书存储。默认情况下,NSURLCredentialStorage 的sharedCredentialStorage 会被
    使用,这与 NSURLConnection 是相同的。 TLSMaximumSupportedProtocol 确定是否支持 SSLProtocol 版本的会话。
    TLSMinimumSupportedProtocol 确定是否支持 SSLProtocol 版本的会话。 Caching Policies
    URLCache 会话使用的缓存。默认情况下,NSURLCache 的sharedURLCache 会被使用,这与 NSURLConnection
    是相同的。
    requestCachePolicy 缓存策略,指定一个请求的缓存响应应该在什么时候返回。这相当于 NSURLRequest 的 cachePolicy
    方法。 Custom Protocols
    protocolClasses 注册 NSURLProtocol 类的特定会话数组。 NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; // 设置同时连接到一台服务器的最大连接数 configuration.HTTPMaximumConnectionsPerHost = 4; // 设置授权信息,WebDav 的身份验证 NSString *username = @"admin";
    NSString *password = @"adminpasswd"; NSString *userPasswordString = [NSString stringWithFormat:@"%@:%@", username, password];
    NSData *userPasswordData = [userPasswordString dataUsingEncoding:NSUTF8StringEncoding];
    NSString *base64EncodedCredential = [userPasswordData base64EncodedStringWithOptions:0];
    NSString *authString = [NSString stringWithFormat:@"Basic: %@", base64EncodedCredential]; // 设置客户端类型 NSString *userAgentString = @"iPhone AppleWebKit"; // 设置请求头 configuration.HTTPAdditionalHeaders = @{@"Accept": @"application/json",
    @"Accept-Language": @"en",
    @"Authorization": authString,
    @"User-Agent": userAgentString};
  • 2.2 URLSession 创建方式

    Important
    The session object keeps a strong reference to the delegate until your app explicitly invalidates the session.
    If you do not invalidate the session by calling the invalidateAndCancel or resetWithCompletionHandler: method,
    your app leaks memory. 一旦指定了 session 的代理,session 会对代理进行强引用,如果不主动取消 session,会造成内存泄漏。 释放强引用的办法: 1> 网络操作完成: 取消 session 标记: session 完成并且无效,已经被取消的会话,无法再次使用。 __weak typeof(self) weakSelf = self;
    [weakSelf.session finishTasksAndInvalidate]; 释放 session: __weak typeof(self) weakSelf = self;
    weakSelf.session = nil; 优点:能够保证下载任务的正常完成。
    坏处:每一次网络访问结束后,都要销毁 session,会造成 session 的重复创建和销毁。 2> 视图控制器销毁之前,将 session 释放掉: viewWillDisappear 方法中,将 session 销毁 [self.session invalidateAndCancel];
    self.session = nil; 好处:只会在视图控制器被销毁之前,才会释放 session,避免重复的创建和销毁。
    缺点:session 被取消后,下载任务同样会被取消(有些版本的 Xcode)。 3> 关于网络访问,通常都是建立一个网路访问的单例: 如果单例的工具类,本身就是 session 的代理,单例会随着引用程序被销毁,才会被释放。就不需要考虑 session 的释放问题。 // 共享会话方式 /*
    为了方便程序员使用,苹果提供了一个全局 session,全局 session 的回调是异步的,所有的任务都是由 session 发起的。要跟进下
    载进度,不能使用全局 session。 该会话使用全局的 Cache,Cookie 和证书。
    */ NSURLSession *urlSession1 = [NSURLSession sharedSession]; // 配置会话方式 /*
    + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration; configuration: + (NSURLSessionConfiguration *)defaultSessionConfiguration;
    + (NSURLSessionConfiguration *)ephemeralSessionConfiguration; NS_AVAILABLE(10_10, 8_0)
    + (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier; 默认会话模式(default):工作模式类似于原来的 NSURLConnection,使用的是基于磁盘缓存的持久化策略,使用用户钥匙串中保
    存的证书进行认证授权。 瞬时会话模式(ephemeral):该模式不使用磁盘保存任何数据。所有和会话相关的缓存,证书,cookies 等都被保存在 RAM 中,
    因此当程序使会话无效,这些缓存的数据就会被自动清空。这对于实现像 "秘密浏览" 功能的功能来说,是很理想的。 后台会话模式(background):该模式在后台完成上传和下载,后台会话不同于常规的普通的会话,它甚至可以在应用程序挂起,退
    出,崩溃的情况下运行上传和下载任务。初始化时指定的标识符,被用于向任何可能在进程外恢复后台传输的守护进程提供上下文。想要查
    看更多关于后台会话的信息,可以查看WWDC Session 204: “What’s New with Multitasking”。
    */ NSURLSession *urlSession2 = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; // 配置会话协议方式 /*
    + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration
    delegate:(nullable id <NSURLSessionDelegate>)delegate
    delegateQueue:(nullable NSOperationQueue *)queue; queue: 注意:下载本身的线程 "只有一条",代理回调可以在 "多个线程" 回调,指定代理执行的队列,不会影响到下载本身的执行。 如何选择队列:网络访问结束后,如果不需要做复杂的操作,可以指定主队列,这样不用考虑线程间通讯 主队列回调: [NSOperationQueue mainQueue] 代理方法在主线程中调用。 下载本身是异步执行的,这一点和 NSURLConnection 一样。
    NSURLSession 即使在主线程回调也不会造成阻塞。 异步回调: [[NSOperationQueue alloc] init]
    nil 代理方法在子线程中调用。 二三两种方式可以创建一个新会话并定制其会话类型。该方式中指定了 session 的委托和委托所处的队列。当不再需要连接时,可以调用
    Session 的 invalidateAndCancel 直接关闭,或者调用 finishTasksAndInvalidate 等待当前 Task 结束后关闭。这时 Delegate
    会收到 URLSession:didBecomeInvalidWithError: 这个事件。Delegate 收到这个事件之后会被解引用。
    */ NSURLSession *urlSession3 = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
  • 2.3 Task 创建方式

    // 数据请求 NSURLSessionDataTask (GET/POST)
    
    // 数据请求 request block 方式
    
    NSURLSessionDataTask *urlSessionDataTask1 = [urlSession1 dataTaskWithRequest:urlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    // block 在子线程中执行
    }]; // 数据请求 request 协议 方式 // 遵守协议 <NSURLSessionDataDelegate>
    NSURLSessionDataTask *urlSessionDataTask2 = [urlSession3 dataTaskWithRequest:urlRequest]; // 数据请求 url block 方式
    /*
    1)该方法内部会自动将请求路径包装成一个请求对象,该请求对象默认包含了请求头信息和请求方法(GET)
    2)如果要发送的是 POST 请求,则不能使用该方法。
    */ NSURLSessionDataTask *urlSessionDataTask3 = [urlSession1 dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    // block 在子线程中执行
    }]; // 数据请求 url 协议 方式
    /*
    1)该方法内部会自动将请求路径包装成一个请求对象,该请求对象默认包含了请求头信息和请求方法(GET)
    2)如果要发送的是 POST 请求,则不能使用该方法。
    */ // 遵守协议 <NSURLSessionDataDelegate>
    NSURLSessionDataTask *urlSessionDataTask4 = [urlSession3 dataTaskWithURL:url]; // 文件下载 NSURLSessionDownloadTask // 文件下载 request block 方式 NSURLSessionDownloadTask *urlSessionDownloadTask1 = [urlSession1 downloadTaskWithRequest:urlRequest completionHandler:^(NSURL * _Nullable location,NSURLResponse * _Nullable response, NSError * _Nullable error) { // block 在子线程中执行
    // location 是下载的文件临时存储路径,下载完成后会被自动删除
    }]; // 文件下载 request 协议 方式 // 遵守协议 <NSURLSessionDownloadDelegate>
    NSURLSessionDownloadTask *urlSessionDownloadTask2 = [urlSession3 downloadTaskWithRequest:urlRequest]; // 文件下载 url block 方式 NSURLSessionDownloadTask *urlSessionDownloadTask3 = [urlSession1 downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) { // block 在子线程中执行
    // location 是下载的文件临时存储路径,下载完成后会被自动删除
    }]; // 文件下载 url 协议 方式 // 遵守协议 <NSURLSessionDownloadDelegate>
    NSURLSessionDownloadTask *urlSessionDownloadTask4 = [urlSession3 downloadTaskWithURL:url]; // 文件下载 resumeData block 方式 NSData *resumeData = nil; NSURLSessionDownloadTask *urlSessionDownloadTask5 = [urlSession1 downloadTaskWithResumeData:resumeData completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    // block 在子线程中执行
    // 断点续传,resumeData 为之前已经下载的数据
    // location 是下载的文件临时存储路径,下载完成后会被自动删除
    }]; // 文件下载 resumeData 协议 方式 // 遵守协议 <NSURLSessionDownloadDelegate>
    NSURLSessionDownloadTask *urlSessionDownloadTask6 = [urlSession3 downloadTaskWithResumeData:resumeData]; // 文件上传 NSURLSessionUploadTask // 文件上传 fromFile block 方式 NSURL *uploadFileUrl = nil; NSURLSessionUploadTask *urlSessionUploadTask1 = [urlSession1 uploadTaskWithRequest:urlRequest fromFile:uploadFileUrl completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    // block 在子线程中执行
    }]; // 文件上传 fromFile 协议 方式 // 遵守协议 <NSURLSessionDataDelegate>
    NSURLSessionUploadTask *urlSessionUploadTask2 = [urlSession3 uploadTaskWithRequest:urlRequest fromFile:uploadFileUrl]; // 文件上传 fromData block 方式 NSData *uploadFileData = nil; NSURLSessionUploadTask *urlSessionUploadTask3 = [urlSession1 uploadTaskWithRequest:urlRequest fromData:uploadFileData completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    // block 在子线程中执行
    }]; // 文件上传 fromData 协议 方式 // 遵守协议 <NSURLSessionDataDelegate>
    NSURLSessionUploadTask *urlSessionUploadTask4 = [urlSession3 uploadTaskWithRequest:urlRequest fromData:uploadFileData]; // 文件上传 Streamed Request 方式 NSURLSessionUploadTask *urlSessionUploadTask5 = [urlSession1 uploadTaskWithStreamedRequest:urlRequest];
    • 2.4 Task 的设置

    // 开始 Task 任务
    [urlSessionDownloadTask1 resume]; // 暂停 Task 任务
    [urlSessionDownloadTask1 suspend]; // 取消 Task 任务 // 完全取消,下次下载又从 0.0% 开始
    [urlSessionDownloadTask1 cancel]; // 可恢复性取消,下次下载可从 保存的 resumeData 处开始
    [urlSessionDownloadTask1 cancelByProducingResumeData:^(NSData * _Nullable resumeData) { }];
    • 2.5 文件下载设置

    // location 是下载的文件临时存储路径,下载完成后会被自动删除。response.suggestedFilename 为服务器端文件名。
    
    NSString *documentsDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:response.suggestedFilename];
    
    // 设置下载的文件存储路径
    /*
    处理下载的数据
    */
    [[NSFileManager defaultManager] copyItemAtPath:location.path toPath:documentsDirPath error:nil];
  • 2.6 文件上传设置

    #define boundary @"myBoundary"
    
    // 设置请求头
    /*
    upload task 不会在请求头里添加 content-type (上传数据类型)字段,@"myBoundary" 为请求体边界,参数可以随便设置,但需一致
    */
    [urlRequest setValue:[NSString stringWithFormat:@"multipart/form-data; charset=utf-8; boundary=%@", boundary] forHTTPHeaderField:@"Content-Type"]; // 设置请求文件参数 NSMutableData *fromBody = [NSMutableData data]; // 参数开始分割线
    /*
    每个参数开始前都需要加
    */
    [fromBody appendData:[[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [fromBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 参数
    [fromBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n%@", @"username", @"jhq"] dataUsingEncoding:NSUTF8StringEncoding]];
    [fromBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 文件开始分割线
    /*
    每个文件开始前都需要加
    */
    [fromBody appendData:[[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [fromBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 文件参数名
    /*
    test.png 为上传后服务器端文件名称
    */
    [fromBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"", @"file", @"test.png"] dataUsingEncoding:NSUTF8StringEncoding]];
    [fromBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 文件的类型
    [fromBody appendData:[[NSString stringWithFormat:@"Content-Type: image/png"] dataUsingEncoding:NSUTF8StringEncoding]];
    [fromBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    [fromBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 待上传文件数据
    /*
    本地待上传的文件路径
    */
    [fromBody appendData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"HQ_0005" ofType:@"jpg"]]];
    [fromBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 结束分割线标记
    [fromBody appendData:[[NSString stringWithFormat:@"--%@--", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [fromBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];

3、NSURLSession 异步 GET 数据请求

  • 3.1 使用 request block 回调方式

    // 设置请求路径
    NSURL *url = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video?type=JSON"]; // 创建请求对象
    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; // 创建会话对象
    NSURLSession *urlSession = [NSURLSession sharedSession]; // 发送请求
    NSURLSessionDataTask *urlSessionDataTask = [urlSession dataTaskWithRequest:urlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { // 处理从服务器下载的数据
    if (error == nil && data != nil) { }
    }]; // 执行任务
    [urlSessionDataTask resume];
  • 3.2 使用 request 协议 方式

    // 遵守协议 <NSURLSessionDataDelegate>
    
    @property(nonatomic, retain)NSMutableData *asyncNetData;
    
    // 设置请求路径
    NSURL *url = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video?type=JSON"]; // 创建请求对象
    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; // 创建会话对象
    NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
    delegate:self
    delegateQueue:[[NSOperationQueue alloc] init]]; // 发送请求
    NSURLSessionDataTask *urlSessionDataTask = [urlSession dataTaskWithRequest:urlRequest]; // 执行任务
    [urlSessionDataTask resume]; // 协议方法 // 接收到服务器的响应
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveResponse:(NSURLResponse *)response
    completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { /*
    需要使用 completionHandler 回调告诉系统应该如何处理服务器返回的数据,默认是取消的。 NSURLSessionResponseCancel = 0, 默认的处理方式,取消
    NSURLSessionResponseAllow = 1, 接收服务器返回的数据
    NSURLSessionResponseBecomeDownload = 2, 变成一个下载请求
    NSURLSessionResponseBecomeStream 变成一个流
    */ // 接收服务器返回的数据
    completionHandler(NSURLSessionResponseAllow); // 异步下载数据源初始化
    self.asyncNetData = [[NSMutableData alloc] init];
    } // 接收到服务器数据
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { // 拼接从服务器下载的数据
    [self.asyncNetData appendData:data];
    } // 服务器的数据加载完毕
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { if (error == nil) { // 处理从服务器下载的数据
    id result = [NSJSONSerialization JSONObjectWithData:self.asyncNetData options:0 error:NULL];
    NSLog(@"异步 GET 网络请求完成: \n%@", result);
    }
    }
  • 3.3 使用 url block 回调方式

    // 设置请求路径
    NSURL *url = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video?type=JSON"]; // 创建会话对象
    NSURLSession *urlSession = [NSURLSession sharedSession]; // 发送请求
    NSURLSessionDataTask *urlSessionDataTask = [urlSession dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { // 处理从服务器下载的数据
    if (error == nil && data != nil) { }
    }];
    // 执行任务
    [urlSessionDataTask resume];
  • 3.4 使用 url 协议 方式

    // 遵守协议 <NSURLSessionDataDelegate>
    
    @property(nonatomic, retain)NSMutableData *asyncNetData;
    
    // 设置请求路径
    NSURL *url = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video?type=JSON"]; // 创建会话对象
    NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
    delegate:self
    delegateQueue:[[NSOperationQueue alloc] init]]; // 发送请求
    NSURLSessionDataTask *urlSessionDataTask = [urlSession dataTaskWithURL:url]; // 执行任务
    [urlSessionDataTask resume]; // 协议方法 // 接收到服务器的响应
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { /*
    需要使用 completionHandler 回调告诉系统应该如何处理服务器返回的数据,默认是取消的。 NSURLSessionResponseCancel = 0, 默认的处理方式,取消
    NSURLSessionResponseAllow = 1, 接收服务器返回的数据
    NSURLSessionResponseBecomeDownload = 2, 变成一个下载请求
    NSURLSessionResponseBecomeStream 变成一个流
    */ // 接收服务器返回的数据
    completionHandler(NSURLSessionResponseAllow); // 异步下载数据源初始化
    self.asyncNetData = [[NSMutableData alloc] init];
    } // 接收到服务器数据
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { // 拼接从服务器下载的数据
    [self.asyncNetData appendData:data];
    } // 服务器的数据加载完毕
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { if (error == nil) { // 处理从服务器下载的数据
    id result = [NSJSONSerialization JSONObjectWithData:self.asyncNetData options:0 error:NULL];
    NSLog(@"异步 GET 网络请求完成: \n%@", result);
    }
    }

4、NSURLSession 异步 POST 数据请求

  • 4.1 使用 request block 回调方式

    // 设置请求路径
    NSURL *url = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video"]; // 创建请求对象
    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url]; // 设置请求方式,默认为 GET 请求
    urlRequest.HTTPMethod = @"POST"; // 设置请求体(请求参数)
    urlRequest.HTTPBody = [@"type=JSON" dataUsingEncoding:NSUTF8StringEncoding]; // 创建会话对象
    NSURLSession *urlSession = [NSURLSession sharedSession]; // 发送请求
    NSURLSessionDataTask *urlSessionDataTask = [urlSession dataTaskWithRequest:urlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { }]; // 执行任务
    [urlSessionDataTask resume];
  • 4.2 使用 request 协议 方式

    // 遵守协议 <NSURLSessionDataDelegate>
    
    // 设置请求路径
    NSURL *url = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video"]; // 创建请求对象
    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url]; // 设置请求方式,默认为 GET 请求
    urlRequest.HTTPMethod = @"POST"; // 设置请求体(请求参数)
    urlRequest.HTTPBody = [@"type=JSON" dataUsingEncoding:NSUTF8StringEncoding]; // 创建会话对象
    NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]]; // 发送请求
    NSURLSessionDataTask *urlSessionDataTask = [urlSession dataTaskWithRequest:urlRequest]; // 执行任务
    [urlSessionDataTask resume]; // 协议方法 // 接收到服务器的响应
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { /*
    需要使用 completionHandler 回调告诉系统应该如何处理服务器返回的数据,默认是取消的。 NSURLSessionResponseCancel = 0, 默认的处理方式,取消
    NSURLSessionResponseAllow = 1, 接收服务器返回的数据
    NSURLSessionResponseBecomeDownload = 2, 变成一个下载请求
    NSURLSessionResponseBecomeStream 变成一个流
    */ // 接收服务器返回的数据
    completionHandler(NSURLSessionResponseAllow);
    } // 接收到服务器数据
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { } // 服务器的数据加载完毕
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { }

5、NSURLSession 文件下载

  • 5.1 使用 request block 回调方式

    // 设置请求路径
    NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/download/file/minion_01.mp4"]; // 创建请求对象
    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; // 创建会话对象
    NSURLSession *urlSession = [NSURLSession sharedSession]; NSURLSessionDownloadTask *urlSessionDownloadTask = [urlSession downloadTaskWithRequest:urlRequest completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error == nil) { // 设置下载的文件存储路径
    NSString *documentsDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:response.suggestedFilename]; // 处理下载的数据
    [[NSFileManager defaultManager] copyItemAtPath:location.path toPath:documentsDirPath error:nil];
    }
    }]; // 执行任务
    [urlSessionDownloadTask resume];
  • 5.2 使用 request 协议 方式

    // 遵守协议 <NSURLSessionDownloadDelegate>
    
    // 设置请求路径
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"]; // 创建请求对象
    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; // 创建会话对象
    NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]]; // 发送请求
    NSURLSessionDownloadTask *urlSessionDownloadTask = [urlSession downloadTaskWithRequest:urlRequest]; // 执行任务
    [urlSessionDownloadTask resume]; // 协议方法 // 监听下载进度,每当写入数据到临时文件时,就会调用一次这个方法
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { // 这次写入多少
    NSLog(@"bytesWritten: %lli", bytesWritten); // 已经写入的大小
    NSLog(@"totalBytesWritten: %lli", totalBytesWritten); // 总大小
    NSLog(@"totalBytesExpectedToWrite: %lli", totalBytesExpectedToWrite); float progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite; dispatch_async(dispatch_get_main_queue(), ^{ // 设置下载进度条
    self.progressView.progress = progress;
    });
    } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { // 设置下载的文件存储路径
    NSString *documentsDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:downloadTask.response.suggestedFilename]; // 处理下载的数据
    [[NSFileManager defaultManager] copyItemAtPath:location.path toPath:documentsDirPath error:nil];
    } // 恢复下载任务时调用
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes { } // 下载完成或中断
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { }
  • 5.3 使用 url block 回调方式

    // 设置请求路径
    NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/download/file/minion_01.mp4"]; // 创建会话对象
    NSURLSession *urlSession = [NSURLSession sharedSession]; NSURLSessionDownloadTask *urlSessionDownloadTask = [urlSession downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error == nil) { // 设置下载的文件存储路径
    NSString *documentsDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:response.suggestedFilename]; // 处理下载的数据
    [[NSFileManager defaultManager] copyItemAtPath:location.path toPath:documentsDirPath error:nil];
    }
    }]; // 执行任务
    [urlSessionDownloadTask resume];
  • 5.4 使用 url 协议 方式

    // 遵守协议 <NSURLSessionDownloadDelegate>
    
    // 设置请求路径
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"]; // 创建会话对象
    NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]]; // 发送请求
    NSURLSessionDownloadTask *urlSessionDownloadTask = [urlSession downloadTaskWithURL:url]; // 执行任务
    [urlSessionDownloadTask resume]; // 协议方法 // 监听下载进度,每当写入数据到临时文件时,就会调用一次这个方法
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
    // 这次写入多少
    NSLog(@"bytesWritten: %lli", bytesWritten); // 已经写入的大小
    NSLog(@"totalBytesWritten: %lli", totalBytesWritten); // 总大小
    NSLog(@"totalBytesExpectedToWrite: %lli", totalBytesExpectedToWrite); float progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite; dispatch_async(dispatch_get_main_queue(), ^{ // 设置下载进度条
    self.progressView.progress = progress;
    });
    } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { // 设置下载的文件存储路径
    NSString *documentsDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:downloadTask.response.suggestedFilename]; // 处理下载的数据
    [[NSFileManager defaultManager] copyItemAtPath:location.path toPath:documentsDirPath error:nil];
    } // 恢复下载任务时调用
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
    } // 下载完成或中断
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { }
  • 5.5 断点续传下载方式

    // 开始下载
    
    _downloadSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:
    
    _resumeTmpPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"resumeData.tmp"];
    
    if ([[NSFileManager defaultManager] fileExistsAtPath:self.resumeTmpPath] == NO) {
    
    	// 多次停止下载,下载的临时文件 会 被自动删除
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"]; self.downloadTask = [self.downloadSession downloadTaskWithURL:url]; // 重新开始下载
    [self.downloadTask resume]; } else { self.resumeData = [NSData dataWithContentsOfFile:self.resumeTmpPath]; // 使用断点下载需要之前下载的临时文件存在,才能继续下载
    self.downloadTask = [self.downloadSession downloadTaskWithResumeData:self.resumeData]; // 断点开始下载
    [self.downloadTask resume];
    } // 暂停下载 [self.downloadTask suspend]; // 继续下载 [self.downloadTask resume]; // 停止下载 // 停止下载。一旦这个 task 被取消了,就无法再恢复
    [self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) { if (resumeData) { self.resumeData = resumeData; [self.resumeData writeToFile:self.resumeTmpPath atomically:YES];
    } self.downloadTask = nil;
    }]; // 协议方法 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { float progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite; dispatch_async(dispatch_get_main_queue(), ^{
    [self.progressBtn q_setButtonWithProgress:progress lineWidth:10 lineColor:nil backgroundColor:[UIColor yellowColor]];
    });
    } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { // 处理下载的数据
    NSString *documentsDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
    [[NSFileManager defaultManager] copyItemAtPath:location.path toPath:documentsDirPath error:NULL]; // 删除断点下载缓存文件
    [[NSFileManager defaultManager] removeItemAtPath:self.resumeTmpPath error:NULL];
    } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes { NSLog(@"恢复下载,已完成:%f%%", (100.0 * fileOffset / expectedTotalBytes));
    } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { NSLog(@"didCompleteWithError --- 中断下载: %@", error.userInfo[NSLocalizedDescriptionKey]); if (error) { // 获取断点数据
    self.resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData];
    } // 下载的临时文件不存在时
    if ([error.localizedFailureReason isEqualToString:@"No such file or directory"]) { // 删除断点下载缓存文件,否则继续断点下载会报错
    [[NSFileManager defaultManager] removeItemAtPath:self.resumeTmpPath error:nil]; [self start];
    }
    }
  • 5.6 后台下载方式

    // 配置为后台下载方式
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"myBackgroundID"];
    _downloadSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[[NSOperationQueue alloc] init]]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"]]; [[self.downloadSession downloadTaskWithRequest:request] resume]; // 协议方法 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { float progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite;
    dispatch_async(dispatch_get_main_queue(), ^{
    [self.progressBtn q_setButtonWithProgress:progress lineWidth:10 lineColor:nil backgroundColor:[UIColor yellowColor]];
    });
    } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSString *documentsDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]
    stringByAppendingPathComponent:downloadTask.response.suggestedFilename]; [[NSFileManager defaultManager] moveItemAtPath:location.path toPath:documentsDirPath error:nil];
    }

6、NSURLSession 文件上传

  • 6.1 使用 formData block 方式

    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://192.168.88.200/upload/upload.php"]];
    urlRequest.HTTPMethod = @"POST"; #define boundary @"uploadBoundary" // 设置请求头
    [urlRequest setValue:[NSString stringWithFormat:@"multipart/form-data; charset=utf-8; boundary=%@", boundary] forHTTPHeaderField:@"Content-Type"]; // 设置请求文件参数
    NSMutableData *formData = [NSMutableData data]; // 参数
    [formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n",@"username"] dataUsingEncoding:NSUTF8StringEncoding]];
    [formData appendData:[[NSString stringWithFormat:@"\r\n%@\r\n", @"qian"] dataUsingEncoding:NSUTF8StringEncoding]]; // 文件
    [formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", @"userfile", @"test1.jpg"] dataUsingEncoding:NSUTF8StringEncoding]];
    [formData appendData:[[NSString stringWithFormat:@"Content-Type: image/jpeg\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
    [formData appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    [formData appendData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"HQ_0005" ofType:@"jpg"]]];
    [formData appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 结束
    [formData appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; NSURLSession *urlSession = [NSURLSession sharedSession]; NSURLSessionUploadTask *urlSessionUploadTask = [urlSession uploadTaskWithRequest:urlRequest fromData:formData completionHandler:^(NSData * _Nullable data,NSURLResponse * _Nullable response, NSError * _Nullable error) { }]; [urlSessionUploadTask resume];
  • 6.2 使用 formData 协议 方式

    // 遵守协议 <NSURLSessionDataDelegate>
    
    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://192.168.88.200/upload/upload.php"]];
    urlRequest.HTTPMethod = @"POST"; #define boundary @"uploadBoundary" // 设置请求头
    [urlRequest setValue:[NSString stringWithFormat:@"multipart/form-data; charset=utf-8; boundary=%@", boundary] forHTTPHeaderField:@"Content-Type"]; // 设置请求文件参数 NSMutableData *formData = [NSMutableData data]; // 参数
    [formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n",@"username"] dataUsingEncoding:NSUTF8StringEncoding]];
    [formData appendData:[[NSString stringWithFormat:@"\r\n%@\r\n", @"qian"] dataUsingEncoding:NSUTF8StringEncoding]]; // 文件
    [formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", @"userfile", @"test2.png"] dataUsingEncoding:NSUTF8StringEncoding]];
    [formData appendData:[[NSString stringWithFormat:@"Content-Type: image/png\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
    [formData appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    [formData appendData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"HQ_0011" ofType:@"png"]]];
    [formData appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 结束
    [formData appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]]; NSURLSessionUploadTask *urlSessionUploadTask = [urlSession uploadTaskWithRequest:urlRequest fromData:formData]; [urlSessionUploadTask resume]; // 协议方法 // 监听上传进度
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { float progress = 1.0 * totalBytesSent / totalBytesExpectedToSend; dispatch_async(dispatch_get_main_queue(), ^{
    [self.progressBtn q_setButtonWithProgress:progress lineWidth:10 lineColor:nil backgroundColor:[UIColor yellowColor]];
    });
    } // 接收到服务器的响应
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { /*
    需要使用 completionHandler 回调告诉系统应该如何处理服务器返回的数据,默认是取消的。 NSURLSessionResponseCancel = 0, 默认的处理方式,取消
    NSURLSessionResponseAllow = 1, 接收服务器返回的数据
    NSURLSessionResponseBecomeDownload = 2, 变成一个下载请求
    NSURLSessionResponseBecomeStream 变成一个流
    */ completionHandler(NSURLSessionResponseAllow); // 异步下载数据源初始化
    self.asyncNetData = [[NSMutableData alloc] init];
    } - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { [self.asyncNetData appendData:data];
    } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { }
  • 6.3 单文件上传封装,使用 formData 方式

    • 文件数据封装使用到第三方框架 QExtension,具体实现代码见 GitHub 源码 QExtension
    #import "NSData+FormData.h"
    
    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://192.168.88.200/upload/upload.php"]];
    urlRequest.HTTPMethod = @"POST"; NSURL *fileURL = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"HQ_0011" ofType:@"png"]];
    NSData *formData = [NSData q_formDataWithRequest:urlRequest text:@"qian" textName:@"username" fileURL:fileURL name:@"userfile" fileName:nil mimeType:nil]; [[[NSURLSession sharedSession] uploadTaskWithRequest:urlRequest fromData:formData completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { }] resume];
- ## 6.4 多文件上传封装,使用 formData 方式
- 文件数据封装使用到第三方框架 QExtension,具体实现代码见 GitHub 源码 QExtension
``` Objective-C
#import "NSData+FormData.h" NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://192.168.88.200/upload/upload-m.php"]];
urlRequest.HTTPMethod = @"POST"; NSURL *fileURL1 = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"HQ_0005" ofType:@"jpg"]];
NSURL *fileURL2 = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"HQ_0011" ofType:@"png"]];
NSData *formData = [NSData q_formDataWithRequest:urlRequest texts:@[@"qian"] textNames:@[@"username"] fileURLs:@[fileURL1, fileURL2] name:@"userfile[]"fileNames:nil mimeTypes:nil]; [[[NSURLSession sharedSession] uploadTaskWithRequest:urlRequest fromData:formData completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { }] resume];
```
- ## 6.5 PUT Block 方式
``` Objective-C
// NSString+Base64.m @implementation NSString (Base64) - (NSString *)q_base64Encode { NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding]; return [data base64EncodedStringWithOptions:0];
} - (NSString *)q_basic64AuthEncode { return [@"BASIC " stringByAppendingString:[self q_base64Encode]];
} @end // ViewController.m // 本地要上传的文件
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"minion.mp4" withExtension:nil]; // 123.mp4 保存到服务器的文件名
NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/uploads/123.mp4"]; // PUT 文件上传,以文件的方式直接写入到 WebDav 服务器中
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
urlRequest.HTTPMethod = @"PUT"; // 服务器验证,用户访问名和密码
[urlRequest setValue:[@"admin:adminpasswd" q_basic64AuthEncode] forHTTPHeaderField:@"Authorization"]; [[[NSURLSession sharedSession] uploadTaskWithRequest:urlRequest fromFile:fileURL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { }] resume];
```
- ## 6.6 PUT 协议 方式
``` Objective-C
// NSString+Base64.m @implementation NSString (Base64) - (NSString *)q_base64Encode { NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding]; return [data base64EncodedStringWithOptions:0];
} - (NSString *)q_basic64AuthEncode {
return [@"BASIC " stringByAppendingString:[self q_base64Encode]];
} @end // ViewController.m // 遵守协议 <NSURLSessionDataDelegate> // 本地要上传的文件
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"minion.mp4" withExtension:nil]; // 123.mp4 保存到服务器的文件名
NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/uploads/123.mp4"]; // PUT 文件上传,以文件的方式直接写入到 WebDav 服务器中
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
urlRequest.HTTPMethod = @"PUT"; // 服务器验证,用户访问名和密码
[urlRequest setValue:[@"admin:adminpasswd" q_basic64AuthEncode] forHTTPHeaderField:@"Authorization"]; NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]]; [[urlSession uploadTaskWithRequest:urlRequest fromFile:fileURL] resume]; // 协议方法 // 监听上传进度
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { float progress = 1.0 * totalBytesSent / totalBytesExpectedToSend; dispatch_async(dispatch_get_main_queue(), ^{
[self.progressBtn q_setButtonWithProgress:progress lineWidth:10 lineColor:nil backgroundColor:[UIColor yellowColor]];
});
} // 接收到服务器的响应
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { /*
需要使用 completionHandler 回调告诉系统应该如何处理服务器返回的数据,默认是取消的。 NSURLSessionResponseCancel = 0, 默认的处理方式,取消
NSURLSessionResponseAllow = 1, 接收服务器返回的数据
NSURLSessionResponseBecomeDownload = 2, 变成一个下载请求
NSURLSessionResponseBecomeStream 变成一个流
*/ completionHandler(NSURLSessionResponseAllow);
} - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { }
```

NSURLSession 网络请求的更多相关文章

  1. 介绍NSURLSession网络请求套件

    昨天翻译了一篇<NSURLSession的使用>的文章,地址:http://www.cnblogs.com/JackieHoo/p/4995733.html,原文是来自苹果官方介绍NSUR ...

  2. NSURLSession网络请求

    个人感觉在网上很难找到很简单的网络请求.或许是我才疏学浅 ,  所有就有了下面这一段 , 虽然都是代码 , 但是全有注释 . //1/获取文件访问路径 NSString *path=@"ht ...

  3. iOS - NSURLSession 网络请求

    前言 NS_CLASS_AVAILABLE(NSURLSESSION_AVAILABLE, 7_0) @interface NSURLSession : NSObject @available(iOS ...

  4. 第八篇、封装NSURLSession网络请求框架

    主要功能介绍: 1.GET请求操作 2.POST请求操作 1.处理params参数(例如拼接成:usename="123"&password="123" ...

  5. iOS - AFNetworking 网络请求

    前言 在 iOS 开发中,一般情况下,简单的向某个 Web 站点简单的页面提交请求并获取服务器的响应,用 Xcode 自带的 NSURLConnection 是能胜任的.但是,在绝大部分下我们所需要访 ...

  6. iOSAFNetworking 网络请求

    前言 在 iOS 开发中,一般情况下,简单的向某个 Web 站点简单的页面提交请求并获取服务器的响应,用 Xcode 自带的 NSURLConnection 是能胜任的.但是,在绝大部分下我们所需要访 ...

  7. CHNetRequest网络请求

    Paste JSON as Code • quicktype 软件的使用 iOS开发:官方自带的JSON使用 JSON 数据解析 XML 数据解析 Plist 数据解析 NetRequest 网络数据 ...

  8. iOS 网络请求NSURLSession

    iOS 7 和 Mac OS X 10.9 Mavericks 中一个显著的变化就是对 Foundation URL 加载系统的彻底重构. 现在已经有人在深入苹果的网络层基础架构的地方做研究了,所以我 ...

  9. IOS网络请求之NSURLSession使用

    前言: 无论是Android还是ios都离不开与服务器交互,这就必须用到网络请求,记得在2013年做iOS的时候那时候用的ASIHTTPRequest框架,现在重新捡起iOS的时候ASIHTTPReq ...

随机推荐

  1. 010. windows10下安装kivy 1.9.1版

    Microsoft Windows [版本 10.0.14393] 以管理员权限打开cmd (c) 2016 Microsoft Corporation. 保留所有权利. 1. C:\Users\LG ...

  2. 第14 章 Spring MVC的工作机制与设计模式

    14.1 Spring MVC的总体设计 要使用SPring MVC,只要在web.xml中配置一个DispatcherServlet. 再定义一个dispatcherServlet-servlet. ...

  3. 第六章 深入分析ClassLoader工作机制

    补充(非书中): Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件).类加载器负责读取Java字节代码,并转换成 java.lan ...

  4. c# 各种tips

    1.lock 类似于 java中的synchronized,对对象或代码块加上互斥锁. 2.c#中的lambda表达式, ForEach(x => f(n)) 3.c# 中的 something ...

  5. 初识 Julia

    Ubuntu 下安装 Julia 环境 sch01ar@ubuntu:~$ sudo apt install julia 安装完成后打开 Julia 的交互式会话 sch01ar@ubuntu:~$ ...

  6. Java的I/O流问题

    一.流的概念        流(stream)的概念源于UNIX中管道(pipe)的概念.在UNIX中,管道是一条不间断的字节流,用来实现程序或进程间的通信,或读写外围设备.外部文件等.        ...

  7. 不用jquery实现tab页切换,刷新,后退,前进状态自动维护 很好用

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. 成都国嵌-嵌入式linux必修实验手册…

    emouse收集整理,转载请注明: emouse的技术专栏 博客园:http://www.cnblogs.com/emouse/ CSDN:http://blog.csdn.net/haozi_198 ...

  9. Hadoop之HDFS(一)HDFS入门及基本Shell命令操作

    1 . HDFS 基本概念 1.1  HDFS 介绍 HDFS 是 Hadoop Distribute File System 的简称,意为:Hadoop 分布式文件系统.是 Hadoop 核心组件之 ...

  10. java Web jsp和servlet的关系

    JSP在本质上就是SERVLET,但是两者的创建方式不一样Servlet完全是JAVA程序代码构成,擅长于流程控制和事务处理,通过Servlet来生成动态网页很不直观JSP由HTML代码和JSP标签构 ...