iOS 开发 NSURLSession使用大全详解(包括请求,上传和断点下载)
来源:互联网 发布:如何网络人肉 编辑:程序博客网 时间:2024/06/03 13:14
NSURLSession基本特点
- 用于替代 NSURLConnection
- 支持后台运行的网络任务
- 暂停、停止、重启网络任务,不再需要 NSOperation 封装
- 请求可以使用同样的配置容器
- 直接使用系统方法可以实现文件上传和下载
- 通过代理方法可以获取文件上传和下载的进度
- block 和代理都对文件上传和下载起作用
- 当文件上传时,block和代理可以同时使用
- 当文件下载时,block和代理不要同时使用
NSURLSession结构图
- 为了方便程序员使用,苹果提供了一个全局 session.
- 所有的 任务(Task) 都是由 session 发起的.
- 所有的任务默认是挂起的,需要 resume.
- session可以自定义,自定义的时候可以同时设置代理.
- session : 如果你要监听上传进度,就不能使用单例session,因为监听上传进度需要使用代理.我们需要自定义session
NSURLSessionConfiguration
用于设置全局的网络会话属性,包括:身份验证,超时时长,缓存策略,Cookie 等.
- 常用属性
三个类构造方法,是为不同的案例设计的.
defaultSessionConfiguration 返回标准配置,具有共享 NSHTTPCookieStorage,NSURLCache 和 NSURLCredentialStorage.
ephemeralSessionConfiguration 返回一个预设配置,没有持久性存储的缓存,Cookie或证书。这对于实现像秘密浏览功能的功能来说,是很理想的.
backgroundSessionConfiguration,独特之处在于,会创建一个后台会话。后台会话不同于常规的,普通的会话,它甚至可以在应用程序挂起,退出,崩溃的情况下运行上传和下载任务。初始化时指定的标识符,被用于向任何可能在进程外恢复后台传输的守护进程提供上下文.
常用 HTTPAdditionalHeaders
//接收数据类型,语言,设备类型configuration.HTTPAdditionalHeaders = @{@"Accept": @"application/json",@"Accept-Language": @"en",@"User-Agent": @"iPhone"};
- NSURLSessionConfiguration使用.
/// 自定义session,设置配置信息 @property (nonatomic, strong) NSURLSession
*session;
//开发中没有额外需求可以不用设置- (NSURLSession *)session { if (_session == nil) { // 创建配置信息 : 开发中只需要使用默认的配置信息就好了 // 当多个任务共享同一个session时候,我们只需要配置一次config,那么有session发起的所有任务,都具备相同的配置信息 NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; // 请求的超时时长是1秒 config.timeoutIntervalForRequest = 5.0; config.requestCachePolicy = NSURLRequestUseProtocolCachePolicy; // 告诉服务器我的设备 config.HTTPAdditionalHeaders = @{@"User-Agent": @"iPhone"}; _session = [NSURLSession sessionWithConfiguration:config]; } return _session;}
NSURLSession的GET请求
- 准备URL
- session
- 发起任务(task)
- 启动任务(resume)
- 处理响应
/// NSURLSession的GET请求 (默认也是GET请求)- (void)demo { // URL NSURL *URL = [NSURL URLWithString:@"http://localhost/php/login/login.php?username=zhangsan&password=zhang"]; // 获取session(单例) NSURLSession *session = [NSURLSession sharedSession]; // 发起任务 NSURLSessionDataTask *dataTask = [session dataTaskWithURL:URL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { // 处理响应 if (error == nil && data != nil) { // 反序列化 NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]; NSLog(@"%@",result); } else { NSLog(@"%@",error); } }]; // 启动任务 [dataTask resume];}
NSURLSession的POST请求
- 准备URL
- 创建可变request (设置请求方法和请求体)
- session
- 发起任务(task)
- 启动任务(resume)
- 处理响应
- (void)login { // URL NSURL *URL = [NSURL URLWithString:@"http://localhost/php/login/login.php"]; // 可变请求对象 NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:URL]; // 设置请求方法 requestM.HTTPMethod = @"POST"; // 设置请求体信息 NSString *body = @"username=zhangsan&password=zhang"; requestM.HTTPBody = [body dataUsingEncoding:NSUTF8StringEncoding]; // session NSURLSession *session = [NSURLSession sharedSession]; // 发起任务 NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:requestM completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { // 处理响应 if (error == nil && data != nil) { // 反序列化 id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]; NSLog(@"%@",result); } else { NSLog(@"%@",error); } }]; // 启动任务 [dataTask resume];}
NSURLSession实现POST文件上传(拼接表单数据)
#import "ViewController.h"@interface ViewController () <NSURLSessionDataDelegate>/// 文件上传的session@property (nonatomic, strong) NSURLSession *session;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad];}- (NSURLSession *)session { if (_session == nil) { // session的配置信息 NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; // 自定义session的同时设置代理 _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[[NSOperationQueue alloc] init]]; } return _session;}- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 参数1 NSString *URLString = @"http://localhost/php/upload/upload.php"; // 参数2 服务器字段名 NSString *serverFileName = @"userfile"; // 参数3 本地文件路径 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"car.jpg" ofType:nil]; [self uploadFileWithURLString:URLString serverFileName:serverFileName filePath:filePath];}/** * 单个文件上传的主方法 * * @param URLString 文件上传的路径 * @param serverFileName 服务器接收文件的字段名 * @param filePath 要上传的文件的路径(有了文件路径就可以获取文件名和文件的二进制) */- (void)uploadFileWithURLString:(NSString *)URLString serverFileName:(NSString *)serverFileName filePath:(NSString *)filePath{ // URL NSURL *URL = [NSURL URLWithString:URLString]; // 可变请求 NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:URL]; // 设置请求头信息 Content-Type: [requestM setValue:@"multipart/form-data; boundary=mac" forHTTPHeaderField:@"Content-Type"]; // 设置请求方法 requestM.HTTPMethod = @"POST"; // session : 如果你要监听上传进度,就不能使用单例session,因为监听上传进度需要使用代理.我们需要自定义session// NSURLSession *session = [NSURLSession sharedSession]; // 文件上传的二进制请求体 NSData *data = [self getHTTPBodyWithServerFileName:serverFileName filePath:filePath]; // 发起上传任务 NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:requestM fromData:data completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { // 处理响应 if (error == nil && data != nil) { // 反序列化 id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]; NSLog(@"%@",result); } else { NSLog(@"%@",error); } }]; // 启动任务 [uploadTask resume];}#pragma mark - NSURLSessionDataDelegate 监听进度/** * 监听进度 * * @param bytesSent 本次发送的字节数 * @param totalBytesSent 总共发送的字节数 * @param totalBytesExpectedToSend 文件的总大小 * */- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { // 计算进度 float progress = (float)totalBytesSent / totalBytesExpectedToSend; NSLog(@"进度 %f",progress);}/** * 获取文件上传的请求体信息(二进制) * * @param serverFileName 服务器接收文件的字段名 * @param filePath 要上传的文件的路径 * * @return 返回文件上传的请求体二进制信息 */- (NSData *)getHTTPBodyWithServerFileName:(NSString *)serverFileName filePath:(NSString *)filePath{ // 定义可变的二进制容器,拼接请求体的二进制信息 NSMutableData *dataM = [NSMutableData data]; // 定义可变字符串,拼接开始的请求体字符串信息 NSMutableString *stringM = [NSMutableString string]; // 拼接文件开始上传的分隔符 [stringM appendString:@"--mac\r\n"]; // 拼接表单数据 [stringM appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n",serverFileName,[filePath lastPathComponent]]; // 拼接要上传的文件的类型 : 如果你不想告诉服务器你的文件类型具体是什么,就可以使用 "Content-Type: application/octet-stream" [stringM appendString:@"Content-Type: image/jpeg\r\n"]; // 拼接单穿的换行 [stringM appendString:@"\r\n"]; // 在这里(拼接文件的二进制数据之前),把前面的请求体字符串转换成二进制,先拼接一次 NSData *stringM_data = [stringM dataUsingEncoding:NSUTF8StringEncoding]; [dataM appendData:stringM_data]; // 拼接文件的二进制数据 NSData *file_data = [NSData dataWithContentsOfFile:filePath]; [dataM appendData:file_data]; NSString *end = @"\r\n--mac--"; [dataM appendData:[end dataUsingEncoding:NSUTF8StringEncoding]]; return dataM.copy;}@end
NSURLSession实现PUT文件上传
- (void)demo { // 1. URL---上传的文件路径 NSString *urlStr = @"http://192.168.3.251/uploads/123.jpg"; NSURL *url = [NSURL URLWithString:urlStr]; // 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 NSURLSession *session = [NSURLSession sharedSession]; // 4. UploadTask NSData *imageData = UIImageJPEGRepresentation(self.imageView.image, 0.75); NSURLSessionUploadTask *upload = [session uploadTaskWithRequest:request fromData:imageData completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error != nil) { NSLog(@"ERROR -> %@", error.localizedDescription); } else { } }]; [upload resume];}
NSURLSession实现文件下载和断点下载
- NSURLSession实现文件下载时,使用代理去下载,内存就不暴涨,而且可以直接的检测到文件下载的进度
- NSURLSessionDownloadTask : 在使用它的时候,如果你遵守了代理,就不能使用回调,一旦使用了回调,代理就无效了.
- 提示 : 代理和回调只能二选一.
- 续传数据时,依然不能使用回调
- 使用session实现文件下载时,文件下载结束之后,默认会删除,所以文件下载结束之后,需要我们手动的保存一份
- 一旦指定了 session 的代理,session会对代理进行强引用,如果不主动取消 session,会造成内存泄漏!
- 下载任务 一旦调用了cancel方法之后,就真的取消了.所以继续下载其实是新建一个下载任务去继续下载文件.
#import "ViewController.h"@interface ViewController () <NSURLSessionDownloadDelegate>/// 自定义session,设置代理@property (nonatomic, strong) NSURLSession *downloadSession;/// 全局的下载任务@property (nonatomic, strong) NSURLSessionDownloadTask *downloadTask;/// 保存续传数据@property (nonatomic, strong) NSData *resumeData;@end@implementation ViewController#pragma mark - 懒加载downloadSession- (NSURLSession *)downloadSession{ if (_downloadSession == nil) { NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; // nil : nil的效果跟 [[NSOperationQueue alloc] init] 是一样的 _downloadSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; } return _downloadSession;}- (void)viewDidLoad { [super viewDidLoad];}#pragma mark - 下载- (IBAction)downloadClick:(id)sender { // 1. URL NSURL *URL = [NSURL URLWithString:@"http://localhost/sogou.zip"]; // 2. 发起下载任务 /* NSURLSessionDownloadTask : 在使用它的时候,如果你遵守了代理,就不能使用回调,一旦使用了回调,代理就无效了. 提示 : 代理和回调只能二选一 */ self.downloadTask = [self.downloadSession downloadTaskWithURL:URL]; // 3. 启动下载任务 [self.downloadTask resume];}#pragma mark - 暂停- (IBAction)pauseClick:(id)sender { [self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) { // resumeData : 续传数据,当暂停下载之后,会把续传的数据回调出去,方便我们做断点下载 // resumeData : 已经下载的字节数... self.resumeData = resumeData; // 拿到续传数据之后,把续传数据保存在沙盒中,APP重启之后,就可以继续下载 NSString *fullPath = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"resume.data"]; [resumeData writeToFile:fullPath atomically:YES]; // 当我们第一次点击暂停时,会成功的回调resumeData数据,但是再次点击时,就回调一个空的resumeData NSLog(@"%tu",resumeData.length); // 为了避免第二次点击暂停时,resumeData为空, self.downloadTask = nil; }]; // suspend有时候不太靠谱,而且如果成功的暂停了,程序退出,再启动,不能实现继续下载// [self.downloadTask suspend]; NSLog(@"暂停");}#pragma mark - 断点下载- (IBAction)resumeClick:(id)sender { // 0.541026 0.543065 // 当内存中没有续传数据时,重新启动程序时 if (self.resumeData == nil) { NSString *fullPath = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"resume.data"]; NSData *resume_data = [NSData dataWithContentsOfFile:fullPath]; if (resume_data == nil) { // 即没有内存续传数据,也没有沙盒续传数据,就续传了 return; } else { // 当沙盒有续传数据时,在内存中保存一份 self.resumeData = resume_data; } } // 续传数据时,依然不能使用回调 // 续传数据时起始新发起了一个下载任务,因为cancel方法是把之前的下载任务干掉了 (类似于NSURLConnection的cancel) // resumeData : 当新建续传数据时,resumeData不能为空,一旦为空,就崩溃 // downloadTaskWithResumeData :已经把Range封装进去了 if (self.resumeData != nil) { self.downloadTask = [self.downloadSession downloadTaskWithResumeData:self.resumeData]; // 重新发起续传任务时,也要手动的启动任务 [self.downloadTask resume]; NSLog(@"继续"); }}#pragma NSURLSessionDownloadDelegate--监听下载进度/// 监听文件下载进度的代理方法- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { // 计算进度 float progress = (float)totalBytesWritten / totalBytesExpectedToWrite; NSLog(@"%f",progress);}#pragma mark - 文件下载结束时的代理方法 (必须实现的)--将文件拷贝到其他地方- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTaskdidFinishDownloadingToURL:(NSURL *)location { // location : 文件下载结束之后的缓存路径 // location.path : 把带协议头的路径转换成字符串路径 (去掉协议头之后的路径) // 使用session实现文件下载时,文件下载结束之后,默认会删除,所以文件下载结束之后,需要我们手动的保存一份 NSLog(@"%@",location.path); NSString *path = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"demo.zip"]; // 文件下载结束之后,需要立即把文件拷贝到一个不会销毁的地方 [[NSFileManager defaultManager] copyItemAtPath:location.path toPath:path error:NULL];}@end
NSURLSession断点下载时的循环引用
- 一旦指定了 session 的代理,session会对代理进行强引用,如果不主动取消 session,会造成内存泄漏!
//两种方法解除循环引用,取其一即可- (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // 在控制器即将销毁时,将sessio立即置为无效 [self.downloadSession invalidateAndCancel];// 在控制器即将销毁时,当下载任务执行结束之后再把session置为无效// [self.downloadSession finishTasksAndInvalidate];}
参考: NSURLSessionConfiguration笔记
0 0
- iOS 开发 NSURLSession使用大全详解(包括请求,上传和断点下载)
- IOS 开发 NSURLConnection使用大全(包括请求,上传,下载)详解
- iOS开发-使用NSURLSession实现文件断点下载,文件离线续传以及图片上传
- iOS使用NSURLSession进行下载(包括后台下载,断点下载)
- 【iOS】NSURLSession断点下载
- iOS -- NSURLSession网络请求,上传,下载
- iOS NSURLSession 实现网络请求-文件下载-上传-后台下载
- iOS之网络—— NSURLSessionDataTask文件离线断点下载、NSURLSession文件上传、AFN基本使用、Cocoapods安装
- iOS开发 ----- 网络请求5 ----- NSURLsession实现上传数据
- 使用AFNetworking 2.0 断点下载 NSURLSession
- iOS NSURLSession使用详解
- iOS关于使用NSURLSession进行大文件下载以及断点下载
- NSURLSession的上传和下载
- iOS 开发 AFNetworking实现网络请求,上传和下载
- iOS开发网络篇—发送GET和POST请求(使用NSURLSession)
- iOS开发网络篇—发送GET和POST请求(使用NSURLSession)
- iOS开发网络篇—发送GET和POST请求(使用NSURLSession)
- iOS开发网络篇—发送GET和POST请求(使用NSURLSession)
- 鼠标移上去以中心为原点慢慢出现
- TextView属性详解
- 【JSONP】通过nodejs做服务器简单模拟实现跨域请求
- doT.js插件的使用
- AndroidStudio怎样导入library项目开源库
- iOS 开发 NSURLSession使用大全详解(包括请求,上传和断点下载)
- 选择排序
- android MediaCodec 音频编解码的实现——转码
- 51nod 1534 棋子游戏
- HDU 4605 Magic Ball Game(可持续化线段树,树状数组,离散化)
- Android源码学习笔记:Handler机制
- vc++ api 双缓冲绘图
- 通过cmd命令行pip安装Python模块 ImportError: No module named XXX
- Can't create handler inside thread that has not called Looper.prepare()