iOS网络请求策略——NSURLSession的基本使用
2018-06-08 本文已影响31人
听海听心
今天和大家一起来学习一下NSURLSession的基本使用,有疏忽的地方,还望各位不吝赐教。
一、NSURLSession的基本使用
/* 关于NSURLSession的介绍在《iOS网络请求策略——NSURLConnection的基本使用》中已经写过了,在此就略过了,直接上诚意。
* 使用步骤
* 1、使用NSURLSession对象创建Task 然后执行Task
* Task的类型
* NSURLSessionTask(抽象类 三个子类)的孩子有NSURLSessionDataTask(发送请求)和NSURLSessionDownloadTask(下载)
* NSURLSessionDataTask的孩子NSURLSessionUploadTask(上传)
*/
1、发送GET请求
// 1、确定URL
NSURL *url = [NSURL URLWithString:@"GET请求URL"];
// 2、创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 创建回话对象 单例
NSURLSession *session = [NSURLSession sharedSession];
// 3、创建Task
/*
* 异步发送 不会阻塞线程 所以在获得响应结果后要进行线程间的通信
* 第一个参数:请求对象
* 第二个参数:completionHandler : 请求完成后调用
* data:响应体信息
* response:响应头信息
* 请求出错信息:error
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
// 如果你发送的是get请求就可以简便方法
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
// 4、执行task
[dataTask resume];
2、发送POST请求
// 1、确定URL
NSURL *url = [NSURL URLWithString:@"POST请求URL"];
// 2、创建请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 3、设置请求方法为POST
request.HTTPMethod = @"POST";
// 4、设置请求体
request.HTTPBody = [@"username=123&pwd=456&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
// 5、创建会话对象
NSURLSession *session = [NSURLSession sharedSession];
// 6、创建Task
/* 异步发送 不会阻塞线程 所以在获得响应结果后要进行线程间的通信
* 第一个参数:请求对象
* 第二个参数:completionHandler : 请求完成后调用
* data:响应体信息
* response:响应头信息
* 请求出错信息:error
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%@",[NSThread currentThread]);
// 8、解析数据
NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
// 7、执行task
[dataTask resume];
3、NSURLSession代理方法
// 1、确认URL
NSURL *url = [NSURL URLWithString:@""];
// 2、创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 3、创建会话对象 设置代理
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 4、创建task对象
NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
// 5、执行task
[task resume];
// 代理方法——<NSURLSessionDataDelegate>
/*
* 接收到服务器响应 默认取消该请求
* 第一个参数:会话对象
* 第二个参数:请求任务
* 第三个参数:响应头信息
* 第四个参数:completionHandler 回调 传给系统
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
NSLog(@"%s",__func__);
/*
NSURLSessionResponseCancel = 0, 取消
NSURLSessionResponseAllow = 1, 接收
NSURLSessionResponseBecomeDownload = 2, 变成下载任务
NSURLSessionResponseBecomeStream NS_ENUM_AVAILABLE(10_11, 9_0) = 3,变成下载任务
*/
completionHandler(NSURLSessionResponseAllow);
}
/*
* 接收到服务器返回的数据 会响应多次
* 第一个参数:会话对象
* 第二个参数:本次接收到的数据
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
NSLog(@"%s",__func__);
// 这样拼接会导致内存问题
[self.fileData appendData:data];
}
/*
* 请求结束时候调用的方法
* 第二个参数:请求任务
* 第三个参数:错误信息
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
NSLog(@"%s",__func__);
NSLog(@"%@",[[NSString alloc] initWithData:self.fileData encoding:NSUTF8StringEncoding]);
}
4、下载大文件——Blcok
// 1、确定URL
NSURL *url = [NSURL URLWithString:@""];
// 2、创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 3、创建会话对象
NSURLSession *session = [NSURLSession sharedSession];
// 4、创建任务Task
/*
* 该方法内部已经完成边接收数据边写入沙盒(tmp)的操作,但是下载之后就被删除了
* 虽然没有内存方面的问题,但是没有办法监听下载进度
* 第一个参数:请求对象
* 第二个参数:completionHandler 回调
* location:
* response: 响应头信息
* error: 错误信息
*/
NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%@---%@",location,[NSThread currentThread]);
// 进行文件的保存操作
// 拼接文件的全路径
NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];
// 剪切文件
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];
NSLog(@"%@",fullPath);
}];
// 开启任务
[task resume];
}
5、下载大文件——代理介绍
// 1、确认URL
NSURL *url = [NSURL URLWithString:@""];
// 2、创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 3、创建会话对象
/*
* 写数据
* 第一个参数:配置信息
* 第二个参数:代理
* 第三个参数:设置代理方法在哪个线程中调用
*/
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 4、创建Task
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
// 5、开启task
[downloadTask resume];
// 代理方法——<NSURLSessionDownloadDelegate>
/*
* 写数据 这里不需要再像dataTask一样把允许下载的信息返回给系统, 默认允许请求
* 第一个参数:会话对象
* 第二个参数:下载任务
* 第三个参数:本次写入的大小
* 第四个参数:本次下载数据的总大小
* 第五个参数:文件的总大小
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
NSLog(@"%f",1.0 * totalBytesWritten / totalBytesExpectedToWrite);
}
/*
* 恢复下载的时候调用该方法
* 第一个参数:会话对象
* 第二个参数:下载任务
* 第三个参数:从什么地方开始下
* 第四个参数:文件的总大小
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{
NSLog(@"%s",__func__);
}
/*
* 下载完成调用
* 第一个参数:会话对象
* 第二个参数:下载任务
* 第三个参数:临时文件的目录
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
NSLog(@"%@",location);
NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
// 注意这个方法是 fileURLWithPath:!!!
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];
NSLog(@"%@",fullPath);
}
/*
* 第一个参数:会话对象
* 第二个参数:下载任务
* 第三个参数:错误信息
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
NSLog(@"%s",__func__);
}
6、NSURLSessionDownloadTask断点下载——代理实现
这种方式只能保证用户在点击了暂停或者取消的时候能继续下载任务,但是一旦应用退出就要从头开始。
/** downloadTask */
@property (nonatomic, strong) NSURLSessionDownloadTask *downloadTask;
/** resumeData */
@property (nonatomic, strong) NSData *resumeData;
/** session */
@property (nonatomic, strong) NSURLSession *session;
// 缺陷 退出应用时没办法接着之前的数据进行下载 如果要实现必须使用之前的dataTask
- (void)beginDownload:(id)sender {
// 确认URL
NSURL *url = [NSURL URLWithString:@""];
// 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 创建会话
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 创建task
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
// 开启任务
[downloadTask resume];
self.session = session;
self.downloadTask = downloadTask;
}
// 暂停可以恢复
- (void)pauseDownload:(id)sender {
[self.downloadTask suspend];
}
// 继续下载
- (void)continueDownload:(id)sender {
// 如果 self.resumeData有值,说明是取消以后继续下载的
if (self.resumeData) {
// 重新创建一个Task
self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];
}
[self.downloadTask resume];
}
// cancel 取消是不能恢复的
// cancelByProducingResumeData:是可以恢复的
- (void)cancelDownload:(id)sender {
// [self.downloadTask cancel];
[self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
// 恢复下载的数据不是已经下载的文件数据,只是记录了相关下载的信息
self.resumeData = resumeData;
}];
}
// 代理方法——<NSURLSessionDownloadDelegate>
// 写数据的时候调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
NSLog(@"%f",1.0 * totalBytesWritten / totalBytesExpectedToWrite);
}
// 恢复下载的时候会调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{
NSLog(@"%s",__func__);
}
// 下载结束的时候会调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
// NSLog(@"%@----%@",location,[NSThread currentThread]);
// 拼接全路径
NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
// 剪切操作
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];
NSLog(@"%@",fullPath);
}
// 出错的时候会调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
NSLog(@"%s",__func__);
}
7、NSURLSessionDataTask离线下载大文件
/** 全路径 */
@property (nonatomic, strong) NSString *fullPath;
/** 文件句柄 */
@property (nonatomic, strong) NSFileHandle *handler;
/** 总数据 */
@property (nonatomic, assign) NSInteger totalSize;
/** 当前下载的数据大小 */
@property (nonatomic, assign) NSInteger currentSize;
/** 任务 */
@property (nonatomic, strong) NSURLSessionDataTask *task;
/** session */
@property (nonatomic, strong) NSURLSession *session;
// 懒加载
- (NSString *)fullPath{
if (!_fullPath) {
// FileName是个宏定义
_fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:FileName];
}
return _fullPath;
}
- (NSURLSession *)session{
if (!_session) {
// 创建会话对象,设置代理
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
- (NSURLSessionDataTask *)task{
if (!_task) {
// 创建URL
NSURL *url = [NSURL URLWithString:@""];
// 创建请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 程序退出再进入的时候去沙盒中去读取文件大小
self.currentSize = [self getFileSize];
// 设置请求头中的range 从读取的文件的位置判断从什么位置开始下载
NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentSize];
[request setValue:range forHTTPHeaderField:@"Range"];
// 创建task
_task = [self.session dataTaskWithRequest:request];
}
return _task;
}
- (NSInteger)getFileSize{
// 获得指定文件路径对应文件的数据大小 在退出之前是需要进行信息采集
NSDictionary *fileInfoDict = [[NSFileManager defaultManager]attributesOfItemAtPath:self.fullPath error:nil];
NSLog(@"%@",fileInfoDict);
NSInteger currentSize = [fileInfoDict[@"NSFileSize"] integerValue];
return currentSize;
}
// 代理方法——<NSURLSessionDataDelegate>
// 得到服务器响应的时候会调用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
// 获取总数据大小
self.totalSize = response.expectedContentLength + self.currentSize;
// 这里判断不能直接return来判断,因为如果采用return completionHandler(NSURLSessionResponseAllow)就不会执行,网络请求都不会发送
if (self.currentSize == 0) {
// 创建空文件
[[NSFileManager defaultManager] createFileAtPath:self.fullPath contents:nil attributes:nil];
}
// 创建文件句柄
self.handler = [NSFileHandle fileHandleForWritingAtPath:self.fullPath];
// 移动文件句柄 这里只是为了在重新创建文件句柄指针的时候能把位置移动到继续下载的地方,其实文件句柄默认是每次都会自动移动的。
[self.handler seekToEndOfFile];
// 允许下载
completionHandler(NSURLSessionResponseAllow);
}
// 接收数据的时候会调用 会调用多次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
// 写入数据
[self.handler writeData:data];
// 获取下载的进度
self.currentSize += data.length;
self.progressView.progress = 1.0 * self.currentSize / self.totalSize;
NSLog(@"%f",1.0 * self.currentSize / self.totalSize);
}
// 请求结束后会调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
NSLog(@"%@",self.fullPath);
//关闭文件句柄 如果不关闭会导致数据丢失
[self.handler closeFile];
self.handler = nil;
}
- (void)dealloc{
// 需要清理 有两个方法都可以进行清理,这里只写了一个
[self.session invalidateAndCancel];
}
// 控制请求开始、暂停和终止
- (void)beginDownload:(id)sender {
// 开启task
[self.task resume];
}
- (void)continueDownload:(id)sender {
NSLog(@"-------------------continue");
[self.task resume];
}
- (void)pauseDownload:(id)sender {
NSLog(@"-------------------pause");
[self.task suspend];
}
- (void)cancelDownload:(id)sender {
NSLog(@"-------------------cancel");
[self.task cancel];
// 这里记得清空,恢复的时候会再次创建self.task,因为是懒加载
self.task = nil;
}
8、文件上传
POST请求上传
/** 请求体用到的宏定义*/
#define kBoundary @"---------------------------67949934127389441772834429"
#define kNewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
/** 会话对象 */
@property (nonatomic, strong) NSURLSession *session;
- (NSURLSession *)session{
if (!_session) {
// 通过配置信息设置
NSURLSessionConfiguration * config = [NSURLSessionConfiguration defaultSessionConfiguration];
// 设置超时时间
config.timeoutIntervalForRequest = 5;
// 允许蜂窝访问
config.allowsCellularAccess = YES;
_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
// 确定URL
NSURL *url = [NSURL URLWithString:@""];
// 创建请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 设置请求方法
request.HTTPMethod = @"POST";
// 设置而请求头
NSString *contentTypeValueStr = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",kBoundary];
[request setValue:contentTypeValueStr forHTTPHeaderField:@"Content-Type"];
// 上传操作
/*
* 第一个参数 : 请求对象
* 第二个参数 : 要上传的数据 请求体 注意请求体要放在这里,不要像NSURLConnection那样放在request对象的请求体中,否则会被忽略。
*/
NSURLSessionUploadTask *task = [self.session uploadTaskWithRequest:request fromData:[self getRequestBody] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
// 开启任务
[task resume];
// 拼接请求体方法
- (NSMutableData *)getRequestBody{
// 拼接请求体数据
NSMutableData *fileData = [NSMutableData data];
/*
--分隔符
Content-Disposition: form-data; name="file"; filename="Snip20160225_341.png"
Content-Type: image/png(MIMEType:大类型/小类型)
空行
文件参数
*/
// name 服务器给我们的
// filename 文件名
// MIMEType
[fileData appendData: [[NSString stringWithFormat:@"--%@",kBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:kNewLine];
[fileData appendData:[@"Content-Disposition: form-data; name=\"file\"; filename=\"icon.png\"" dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:kNewLine];
[fileData appendData:[@"Content-Type: image/png" dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:kNewLine];
[fileData appendData:kNewLine];
UIImage *image = [UIImage imageNamed:@"icon"];
NSData *imageData = UIImagePNGRepresentation(image);
[fileData appendData:imageData];
[fileData appendData:kNewLine];
/*
2)非文件参数
--分隔符
Content-Disposition: form-data; name="username"
空行
123456
*/
[fileData appendData: [[NSString stringWithFormat:@"--%@",kBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:kNewLine];
[fileData appendData:[@"Content-Disposition: form-data; name=\"username\"" dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:kNewLine];
[fileData appendData:kNewLine];
[fileData appendData:[@"123" dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:kNewLine];
/*
* 3)结尾标识
--分隔符--
*/
[fileData appendData: [[NSString stringWithFormat:@"--%@--",kBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
return fileData;
}
/* 设置代理,监听上传进度 —— <NSURLSessionDataDelegate>
* 第一个参数:本次发送的数据
* 第二个参数:上传完成的数据大小
* 第三个参数:文件的总数据大小
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
NSLog(@"%f",1.0 * totalBytesSent / totalBytesExpectedToSend);
}