用于设置全局的网络会话属性,包括:身份验证,超时时长,缓存策略,Cookie 等.
三个类构造方法,是为不同的案例设计的.
defaultSessionConfiguration 返回标准配置,具有共享 NSHTTPCookieStorage,NSURLCache 和 NSURLCredentialStorage.
ephemeralSessionConfiguration 返回一个预设配置,没有持久性存储的缓存,Cookie或证书。这对于实现像秘密浏览功能的功能来说,是很理想的.
backgroundSessionConfiguration,独特之处在于,会创建一个后台会话。后台会话不同于常规的,普通的会话,它甚至可以在应用程序挂起,退出,崩溃的情况下运行上传和下载任务。初始化时指定的标识符,被用于向任何可能在进程外恢复后台传输的守护进程提供上下文.
常用 HTTPAdditionalHeaders
//接收数据类型,语言,设备类型
configuration.HTTPAdditionalHeaders = @{
@"Accept": @"application/json",
@"Accept-Language": @"en",
@"User-Agent": @"iPhone"
};
/// 自定义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请求 (默认也是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];
}
- (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];
}
#import "ViewController.h"
@interface ViewController ()
/// 文件上传的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 *)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
- (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];
}
#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 *)downloadTask
didFinishDownloadingToURL:(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
//两种方法解除循环引用,取其一即可
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// 在控制器即将销毁时,将sessio立即置为无效
[self.downloadSession invalidateAndCancel];
// 在控制器即将销毁时,当下载任务执行结束之后再把session置为无效
// [self.downloadSession finishTasksAndInvalidate];
}
参考: NSURLSessionConfiguration笔记