网络篇 - 07.NSURLSession的应用场景

1.大文件下载

  • 创建下载任务
-(void)downloadLargeFile{
    // 1.创建请求对象
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    // 2.创建下载任务
    /* 参数解释:
     第一个参数:设置session的配置信息,一般传默认值
     第二个参数:设置代理(遵守NSURLSessionDownloadDelegate协议)
     第三个参数:设置代理方法在哪个线程中执行
     */
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
    NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
    // 3.执行任务
    [downloadTask resume];
}
  • 控制任务状态
    • 注意点: 任务一旦取消, 就不能恢复了
    • 如果是调用cancelByProducingResumeData方法, 方法内部会回调一个block, 在block中会将resumeData传递给我们
    • resumeData中就保存了当前下载任务的配置信息(下载到什么地方, 从什么地方恢复等等)
//task有三种状态
-(void)tastState{
    // 取消
    [downloadTask cancel];
    [downloadTask cancelByProducingResumeData:^(NSData *resumeData) {
    }];
    // 暂停
    [downloadTask suspend];
    // 设置任务从上次下载到的地方开始继续下载
    [session downloadTaskWithResumeData:self.resumeData];
    [downloadTask resume];
}
  • 监听下载进度(NSURLSessionDownloadDelegate使用)
    • 首先控制器需遵守NSURLSessionDownloadDelegate协议
    • 在接受数据的代理方法中监听下载数据状况
    • 在下载完成后将保存的数据放到应用程序的缓存当中
#pragma mark - NSURLSessionDownloadDelegate
// 接收到服务器返回的数据时调用
// 该方法会调用一次或多次
// didWriteData : 此次接收到的数据大小
// totalBytesWritten : 总共接收到的数据大小
// totalBytesExpectedToWrite : 服务器总共会返回给我们的文件大小
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
    // 监听下载进度
}

// 写入完成时调用
// location为写入数据完成后的文件在系统tmp文件中的位置
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
    // 将下载好的文件, 移动到caches目录下
}

// 恢复下载时调用
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{

}

// 完成下载后调用
// 当该方法中的error有值代表下载错误
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    // 在此方法中处理错误信息
    NSLog(@"%s",__func__);
}

2.NSURLsession断点下载功能

  • 要想实现断点下载的功能,必须使用二进制任务模式,监听下载进度
  • 注意:需要设置请求从上次下载完的地方开始任务
  • 另外,代理方法中在获取到服务器响应的代理方法-(void)URLSession:(NSURLSession )session dataTask:(NSURLSessionDataTask )dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler,默认状态时不会接受数据的,必须设置completionHandler的状态为接受模式,才会执行接受数据的代理方法
  • 下载完毕后需要关闭输出流
#import "ViewController.h"
#import "NSString+SandboxPath.h"
@interface ViewController ()<NSURLSessionDataDelegate>
@property (nonatomic, assign)NSUInteger totalLength; /**< 总大小 */
@property (nonatomic, assign)NSUInteger currentLength; /**< 当前已经下载的大小 */

@property (nonatomic, strong) NSOutputStream *outputStream ; /**< 输出流 */

@property (nonatomic, copy) NSString *path; /**< 文件路径 */
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 1.拼接文件路径
    self.path = [@"minion_02.mp4" cacheDir];
    NSLog(@"%@",self.path);
    // 2.获取当前下载文件的大小
    self.currentLength = [self getFileSizeWithPath:self.path];
    // 3.获取URL
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    // 4.设置请求头
    NSString *range = [NSString stringWithFormat:@"bytes:%ld-",self.currentLength];
    [request setValue:range forHTTPHeaderField:@"Range"];
    // 5.创建任务
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
    // 6.执行任务
    [task resume];
}

// 获取文件大小
- (NSUInteger)getFileSizeWithPath:(NSString *)path
{
    NSUInteger currentSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil][NSFileSize] integerValue];
    return currentSize;
}

#pragma mark - NSURLSessionDataDelegate
// 收到服务器响应后开始调用
/*
    typedef NS_ENUM(NSInteger, NSURLSessionResponseDisposition) {
        NSURLSessionResponseCancel = 0,       取消接受数据
        NSURLSessionResponseAllow = 1,        允许接受数据
        NSURLSessionResponseBecomeDownload = 2, 转移到下载任务
     }
    */
// completionHandler默认是传入0,即取消状态的,所以必须设置为接受状态才会执行接受数据的代理方法
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
    // 允许接受数据
    completionHandler(NSURLSessionResponseAllow);
    // 计算文件总大小
    self.totalLength = response.expectedContentLength + self.currentLength;
    NSLog(@"%lld",response.expectedContentLength);
    // 开启输出流
    self.outputStream = [NSOutputStream outputStreamToFileAtPath:self.path append:YES];
    [self.outputStream open];
}
// 开始接受数据后调用(会调用一次或多次)
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
    // 获取当前下载数据大小
    self.currentLength += data.length;
    // 计算进度
    CGFloat progress = 1.0 * self.currentLength / self.totalLength;
    NSLog(@"%lf",progress);
    // 写如数据
    [self.outputStream write:data.bytes maxLength:data.length];
}
// 文件下载完毕后调用
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    // 关闭输出流
    [self.outputStream close];
}
@end

3.文件上传(基本使用)

  • 首先,必须设置请求体格式,要求除了分割线可以随便写以外,其余格式必须完全一模一样才能够上传成功
  • 其次,请求体数据拼接好后,不能直接使用request.HTTPBody = body;赋值,也就是说,如果利用NSURLSessionUploadTask上传文件, 那么请求体必须写在fromData参数中, 不能设置在request中
  • 如果设置在request中会被忽略
#define ZJBoundary @"MrRight"
#define ZJEncode(string) [string dataUsingEncoding:NSUTF8StringEncoding]
#define ZJNewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
-(void)upload{
    // 1.创建url
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/upload"];
    // 2.根据url创建请求对象
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    // 2.1设置请求头
    request.HTTPMethod = @"POST";
    [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", ZJBoundary] forHTTPHeaderField:@"Content-Type"];

    // 2.2设置请求体
    NSMutableData *body = [NSMutableData data];
    // 文件参数
    // 分割线
    [body appendData:ZJEncode(@"--")];
    [body appendData:ZJEncode(ZJBoundary)];
    [body appendData:ZJNewLine];

    // 文件参数名
    [body appendData:ZJEncode([NSString stringWithFormat:@"Content-Disposition: form-data; name=\"file\"; filename=\"test.png\""])];
    [body appendData:ZJNewLine];

    // 文件的类型
    [body appendData:ZJEncode([NSString stringWithFormat:@"Content-Type: image/png"])];
    [body appendData:ZJNewLine];

    // 文件数据
    [body appendData:ZJNewLine];
    [body appendData:[NSData dataWithContentsOfFile:@"/Users/liuzhouji/Desktop/按钮属性.png"]];
    [body appendData:ZJNewLine];

    // 结束标记
    /*
     --分割线--\r\n
     */
    [body appendData:ZJEncode(@"--")];
    [body appendData:ZJEncode(ZJBoundary)];
    [body appendData:ZJEncode(@"--")];
    [body appendData:ZJNewLine];

    // 注意点: The body stream and body data in this request object are ignored.
    // 也就是说, 如果利用NSURLSessionUploadTask上传文件, 那么请求体必须写在fromData参数中, 不能设置在request中
    // 如果设置在request中会被忽略
//    request.HTTPBody = body;

    // 3.创建任务
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];

    // 4.执行任务
    [task resume];
}

4.断点上传

  • 要想实现断点上传功能,创建URL和请求设置和例三一致,只有以下两点不太一样
  • 首先设置代理,让当前控制器遵守NSURLSessionTaskDelegate协议
  • 实现代理方法监听上传进度
    // 1.创建session
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    // 2.根据Session创建Task
    /*
     第一个参数: 需要请求的地址/请求头/请求体
     第二个参数: 是需要数据的二进制数据, 但是这个数据时拼接之后的数据
     */
    NSURLSessionUploadTask *task =[session uploadTaskWithRequest:request fromData:body];
    [task resume];
#pragma mark - NSURLSessionTaskDelegate
// 上传过程中调用
// bytesSent: 当前这一次上传的数据大小
// totalBytesSent: 总共已经上传的数据大小
// totalBytesExpectedToSend: 需要上传文件的大小
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
//    NSLog(@"didSendBodyData");
    NSLog(@"%f", 1.0 * totalBytesSent / totalBytesExpectedToSend);
}

// 请求完毕时调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSLog(@"didCompleteWithError");
}

你可能感兴趣的:(网络篇)