网络请求之NSURLSession

  • 使用步骤

    • 使用NSURLSession对象创建Task,然后执行Task
  • Task的类型

NSURLSessionTask.png

获得获得NSURLSession

  • 获得共享的Session
+ (NSURLSession *)sharedSession;
  • 自定义Session
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id <NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue;

NSURLSessionTask

  • 常见方法
- (void)suspend; // 暂停
- (void)resume; // 恢复
- (void)cancel; // 取消
@property (readonly, copy) NSError *error; // 错误
@property (readonly, copy) NSURLResponse *response; // 响应

NSURLSessionDownloadTask

  • 常见方法
- (void)cancelByProducingResumeData:(void (^)(NSData *resumeData))completionHandler; // 取消任务

1. NSURLSessionDownloadTask大文件下载(block)

#define SCREENWIDTH [UIScreen mainScreen].bounds.size.width
#define SCREENHEIGHT [UIScreen mainScreen].bounds.size.height

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic,strong) UIButton * btn;

@end

@implementation ViewController

-(UIButton *)btn
{
    if (!_btn) {
        _btn = [UIButton buttonWithType:UIButtonTypeCustom];
        
        _btn.frame = CGRectMake(SCREENWIDTH / 2 - 75, SCREENHEIGHT / 2 - 15, 150, 30);
        [_btn setTitle:@"点击" forState:UIControlStateNormal];
        [_btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [_btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
        
    }
    return _btn;
}

-(void)clickBtn:(UIButton *)btn
{
    [self download];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    [self.view addSubview:self.btn];
}

// 优点: 不需要担心内存问题
// 缺点: 无法监听文件下载进度
-(void)download
{
    // 1. 确定URL
    NSURL * url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_03.png"];
    
    // 2. 创建请求对象
    NSURLRequest * request = [NSURLRequest requestWithURL:url];
    
    // 3. 创建session
    NSURLSession * session = [NSURLSession sharedSession];
    
    // 4. 创建Task
    /*
     第一参数: 请求对象
     第二参数: completionHandler 回调
            location: 文件的临时存储路径
            response: 响应头信息
            error:    错误信息
     */
    // 该方法内部已经实现了边接受数据边写沙盒(tmp)的操作
    NSURLSessionDownloadTask * downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        // 6. 处理数据
//        NSLog(@"%@------%@",location,[NSThread currentThread]);
        
        
        // 6.1 拼接文件全路径
        NSString * fullPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        // 拼接文件名
        fullPath = [fullPath stringByAppendingPathComponent:response.suggestedFilename];
        
        NSLog(@"开始前:%@",fullPath);
        
        // 6.2 剪切文件
        /*
         第一参数: 需要剪切的文件在哪里
         第二参数: 该文件要剪切到什么地方去
         第三参数: 错误信息
         */
        [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];
        
        NSLog(@"%@",fullPath);
    }];
    
    // 5. 执行Task
    [downloadTask resume];
    
}

@end

2. NSURLSessionDownloadTask大文件下载(delegate)


#define SCREENWIDTH [UIScreen mainScreen].bounds.size.width
#define SCREENHEIGHT [UIScreen mainScreen].bounds.size.height

#import "ViewController.h"

@interface ViewController ()<NSURLSessionDownloadDelegate>

@property (nonatomic,strong) UIButton * btn;

@end

@implementation ViewController

-(UIButton *)btn
{
    if (!_btn) {
        _btn = [UIButton buttonWithType:UIButtonTypeCustom];
        
        _btn.frame = CGRectMake(SCREENWIDTH / 2 - 75, SCREENHEIGHT / 2 - 15, 150, 30);
        [_btn setTitle:@"点击" forState:UIControlStateNormal];
        [_btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [_btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
        
    }
    return _btn;
}

-(void)clickBtn:(UIButton *)btn
{
    [self delegate];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    [self.view addSubview:self.btn];
}

-(void)delegate
{
    // 1. 确定URL
    NSURL * url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_03.png"];
    
    // 2. 创建请求对象
    NSURLRequest * request = [NSURLRequest requestWithURL:url];
    
    // 3. 创建session
    NSURLSession * session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    
    // 4. 创建Task
    NSURLSessionDownloadTask * downloadTask = [session downloadTaskWithRequest:request];
    
    // 5. 执行Task
    [downloadTask resume];
    
}

#pragma mark - NSURLSessionDownloadDelegate
/**
 *  写数据
 *
 *  @param session                    会话对象
 *  @param downloadTask               下载任务
 *  @param bytesWritten               本次写入的数据大小
 *  @param totalBytesWritten          下载的数据总大小
 *  @param totalBytesExpectedToWrite  文件的总大小
 */
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    // 1. 获得文件的下载进度
    NSLog(@"%f",1.0 * totalBytesWritten / totalBytesExpectedToWrite);
}

/**
 *  当恢复下载的时候调用该方法
 *
 *  @param session                    会话对象
 *  @param downloadTask               下载任务
 *  @param fileOffset                 从什么地方(恢复)下载
 *  @param expectedTotalBytes         文件的总大小
 */
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
    NSLog(@"%s",__func__);
}

/**
 *  当下载完成的时候调用该方法
 *
 *  @param session                    会话对象
 *  @param downloadTask               下载任务
 *  @param location                   文件的临时存储路径
 */
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    NSLog(@"%@",location);
    
    // 1. 拼接文件全路径
    NSString * fullPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    
    // 2.拼接文件
    fullPath = [fullPath stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
    
    // 3. 剪切文件
    [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];
    NSLog(@"保存缓存:%@",fullPath);
}

/**
 *  当请求结束的时候调用该方法
 *
 *  @param session            会话对象
 *  @param task               下载任务
 *  @param error              错误信息
 */
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSLog(@"%s",__func__);
}


@end

3. NSURLSessionDownloadTask大文件下载(断点下载)

#define SCREENWIDTH [UIScreen mainScreen].bounds.size.width
#define SCREENHEIGHT [UIScreen mainScreen].bounds.size.height

#import "ViewController.h"

@interface ViewController ()<NSURLSessionDownloadDelegate>

@property (nonatomic,strong) UIButton * startBtn;

@property (nonatomic,strong) UIButton * pauseBtn;

@property (nonatomic,strong) UIButton * cancelBtn;

@property (nonatomic,strong) UIButton * continueBtn;

// session
@property (nonatomic,strong) NSURLSession * session;
// Task
@property (nonatomic,strong) NSURLSessionDownloadTask * downloadTask;
@property (nonatomic,strong) NSData * newrResumeData;

@end

@implementation ViewController

-(UIButton *)startBtn
{
    if (!_startBtn) {
        _startBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        
        _startBtn.frame = CGRectMake(SCREENWIDTH / 2 - 75, 80, 150, 30);
        [_startBtn setTitle:@"开始下载" forState:UIControlStateNormal];
        [_startBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [_startBtn setTitleColor:[UIColor blueColor] forState:UIControlStateSelected];
        [_startBtn addTarget:self action:@selector(clickStartBtn:) forControlEvents:UIControlEventTouchUpInside];
        
    }
    return _startBtn;
}

-(UIButton *)pauseBtn
{
    if (!_pauseBtn) {
        _pauseBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        
        _pauseBtn.frame = CGRectMake(SCREENWIDTH / 2 - 75, 130, 150, 30);
        [_pauseBtn setTitle:@"暂停下载" forState:UIControlStateNormal];
        [_pauseBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [_pauseBtn addTarget:self action:@selector(clickPauseBtn:) forControlEvents:UIControlEventTouchUpInside];
        
    }
    return _pauseBtn;
}

-(UIButton *)cancelBtn
{
    if (!_cancelBtn) {
        _cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        
        _cancelBtn.frame = CGRectMake(SCREENWIDTH / 2 - 75, 190, 150, 30);
        [_cancelBtn setTitle:@"取消下载" forState:UIControlStateNormal];
        [_cancelBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [_cancelBtn addTarget:self action:@selector(clickCancelBtn:) forControlEvents:UIControlEventTouchUpInside];
        
    }
    return _cancelBtn;
}

-(UIButton *)continueBtn
{
    if (!_continueBtn) {
        _continueBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        
        _continueBtn.frame = CGRectMake(SCREENWIDTH / 2 - 75, 250, 150, 30);
        [_continueBtn setTitle:@"继续下载" forState:UIControlStateNormal];
        [_continueBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [_continueBtn addTarget:self action:@selector(clickContinueBtn:) forControlEvents:UIControlEventTouchUpInside];
        
    }
    return _continueBtn;
}

-(void)clickStartBtn:(UIButton *)btn
{
    //    [self download];
    
    [self delegate];
    
}

// 暂停,可以恢复
-(void)clickPauseBtn:(UIButton *)btn
{
    NSLog(@"------暂停------");
    [self.downloadTask suspend];
}

// cancel : 取消,不能恢复
// cancelByProducingResumeData: 是可以恢复的
-(void)clickCancelBtn:(UIButton *)btn
{
    NSLog(@"------取消------");
//    [self.downloadTask cancel];
    
    // 恢复下载的数据 !=(不等于) 文件数据
    [self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
        self.newrResumeData = resumeData;
    }];
}

-(void)clickContinueBtn:(UIButton *)btn
{
    NSLog(@"------继续------");
    
    if (self.newrResumeData != nil) {
        self.downloadTask = [self.session downloadTaskWithResumeData:self.newrResumeData];
    }
    
    [self.downloadTask resume];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    [self.view addSubview:self.startBtn];
    [self.view addSubview:self.pauseBtn];
    [self.view addSubview:self.cancelBtn];
    [self.view addSubview:self.continueBtn];
}

// 优点: 不需要担心内存问题
// 缺点: 无法监听文件下载进度
-(void)download
{
    // 1. 确定URL
    NSURL * url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_03.mp4"];
    
    // 2. 创建请求对象
    NSURLRequest * request = [NSURLRequest requestWithURL:url];
    
    // 3. 创建session
    NSURLSession * session = [NSURLSession sharedSession];
    
    // 4. 创建Task
    /*
     第一参数: 请求对象
     第二参数: completionHandler 回调
     location:
     response: 响应头信息
     error:    错误信息
     */
    // 该方法内部已经实现了边接受数据边写沙盒(tmp)的操作
    NSURLSessionDownloadTask * downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        // 6. 处理数据
        //        NSLog(@"%@------%@",location,[NSThread currentThread]);
        
        
        // 6.1 拼接文件全路径
        NSString * fullPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        // 拼接文件名
        fullPath = [fullPath stringByAppendingPathComponent:response.suggestedFilename];
        
        NSLog(@"开始前:%@",fullPath);
        
        // 6.2 剪切文件
        /*
         第一参数: 需要剪切的文件在哪里
         第二参数: 该文件要剪切到什么地方去
         第三参数: 错误信息
         */
        [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];
        
        NSLog(@"%@",fullPath);
    }];
    
    // 5. 执行Task
    [downloadTask resume];
    
}


-(void)delegate
{
    // 1. 确定URL
    NSURL * url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_03.mp4"];
    
    // 2. 创建请求对象
    NSURLRequest * request = [NSURLRequest requestWithURL:url];
    
    // 3. 创建session
    self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    
    // 4. 创建Task
    NSURLSessionDownloadTask * downloadTask = [self.session downloadTaskWithRequest:request];
    
    // 5. 执行Task
    [downloadTask resume];
    
    self.downloadTask = downloadTask;
    
}

#pragma mark - NSURLSessionDownloadDelegate
/**
 *  写数据
 *
 *  @param session                    会话对象
 *  @param downloadTask               下载任务
 *  @param bytesWritten               本次写入的数据大小
 *  @param totalBytesWritten          下载的数据总大小
 *  @param totalBytesExpectedToWrite  文件的总大小
 */
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    // 1. 获得文件的下载进度
    NSLog(@"%f",1.0 * totalBytesWritten / totalBytesExpectedToWrite);
}

/**
 *  当恢复下载的时候调用该方法
 *
 *  @param session                    会话对象
 *  @param downloadTask               下载任务
 *  @param fileOffset                 从什么地方(恢复)下载
 *  @param expectedTotalBytes         文件的总大小
 */
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
    NSLog(@"%s",__func__);
}

/**
 *  当下载完成的时候调用该方法
 *
 *  @param session                    会话对象
 *  @param downloadTask               下载任务
 *  @param location                   文件的临时存储路径
 */
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    NSLog(@"%@",location);
    
    // 1. 拼接文件全路径
    NSString * fullPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    
    // 2.拼接文件
    fullPath = [fullPath stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
    
    // 3. 剪切文件
    [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];
    NSLog(@"保存缓存:%@",fullPath);
}

/**
 *  当请求结束的时候调用该方法
 *
 *  @param session            会话对象
 *  @param task               下载任务
 *  @param error              错误信息
 */
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSLog(@"%s",__func__);
}


@end

4. NSURLSessionDataTask实现大文件下载

#define SCREENWIDTH [UIScreen mainScreen].bounds.size.width
#define SCREENHEIGHT [UIScreen mainScreen].bounds.size.height

#import "ViewController.h"

@interface ViewController ()<NSURLSessionDataDelegate>

@property (nonatomic,strong) UIButton * btn;

/**  下载进度条  */
@property (nonatomic,strong) UIProgressView * progressView;

/** 文件句柄 */
@property (nonatomic,strong) NSFileHandle * handle;
/** 下载总大小 */
@property (nonatomic,assign) NSInteger totalSize;

/** 当前下载大小 */
@property (nonatomic,assign) NSInteger currentSize;

/** 文件的全路径  */
@property (nonatomic,strong)  NSString * fullPath;

@end

@implementation ViewController

-(UIButton *)btn
{
    if (!_btn) {
        _btn = [UIButton buttonWithType:UIButtonTypeCustom];
        
        _btn.frame = CGRectMake(SCREENWIDTH / 2 -75 , SCREENHEIGHT / 2 -15, 150, 30);
        [_btn setTitle:@"点击" forState:UIControlStateNormal];
        [_btn setTitleColor:[UIColor redColor]  forState:UIControlStateNormal];
        [_btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
        
    }
    return _btn;
}

- (UIProgressView *)progressView
{
    if (!_progressView) {
        _progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(SCREENWIDTH / 2 - 100, 80, 200, 30)];
        _progressView.progress = 0;
        
    }
    return _progressView;
}

-(void)clickBtn:(UIButton *)btn
{
    // 1. 确定URL
    NSURL * url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_03.mp4"];
    
    // 2. 创建请求对象
    NSURLRequest * request = [NSURLRequest requestWithURL:url];
    
    // 3. 创建会话对象,设置代理
    /*
     第一参数: 配置信息  [NSURLSessionConfiguration defaultSessionConfiguration]  默认
     第二参数: 代理
     第三参数: 设置代理方法在哪个线程中调用
     */
    NSURLSession * session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    
    
    // 4. 创建Task
    NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:request];
    
    // 5. 执行Task
    [dataTask resume];
    
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    [self.view addSubview:self.btn];
    [self.view addSubview:self.progressView];
}

#pragma mark - NSURLSessionDataDelegate
/**
 *  1. 接受到服务器的响应 它默认会取消该请求
 *
 *  @param session               会话对象
 *  @param dataTask              请求任务
 *  @param response              响应头信息
 *  @param completionHandler     回调  传给系统
 */
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    
    // 获得文件的总大小
    self.totalSize = response.expectedContentLength;
    
    // 获得文件全路径
    self.fullPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    // 拼接文件名称
    self.fullPath = [self.fullPath stringByAppendingPathComponent:response.suggestedFilename];
    
    // 创建一个空的文件
    [[NSFileManager defaultManager] createFileAtPath:self.fullPath contents:nil attributes:nil];
    
    // 创建文件句柄
    self.handle = [NSFileHandle fileHandleForWritingAtPath:self.fullPath];
    
    // 移动指针
    [self.handle seekToEndOfFile];
    
    /*
     NSURLSessionResponseCancel = 0,取消 默认
     NSURLSessionResponseAllow = 1, 接收
     NSURLSessionResponseBecomeDownload = 2, 变成下载任务
     NSURLSessionResponseBecomeStream        变成下载任务
     */
    completionHandler(NSURLSessionResponseAllow);
}

/**
 *  接收到服务器返回的数据  调用多次
 *
 *  @param session          会话对象
 *  @param dataTask         请求任务
 *  @param data             本次下载的数据
 */
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
    
    
    
    // 写入数据到文件
    [self.handle writeData:data];
    
    // 当前下载的大小
    self.currentSize += data.length;
    
    CGFloat progressF = 1.0 * self.currentSize / self.totalSize;
    
    // 进度条显示进度状态
    self.progressView.progress = progressF;
    
    // 计算文件的下载进度
    NSLog(@"%f",1.0 * self.currentSize / self.totalSize);
    
}

/**
 *  请求结束或者是失败的时候调用
 *
 *  @param session          会话对象
 *  @param task             请求任务
 *  @param error            错误信息
 */
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSLog(@"下载好后的文件:%@",self.fullPath);
    
    // 关闭文件句柄
    [self.handle closeFile];
    self.handle = nil;
    
}

@end

5. NSURLSessionDataTask离线断点下载(断点续传)

#define SCREENWIDTH [UIScreen mainScreen].bounds.size.width
#define SCREENHEIGHT [UIScreen mainScreen].bounds.size.height

#define FileName @"03.mp4"

#define TOTASLSIZE @"totalSize"

#import "ViewController.h"

@interface ViewController ()<NSURLSessionDataDelegate>

@property (nonatomic,strong) UIButton * startBtn;

@property (nonatomic,strong) UIButton * pauseBtn;

@property (nonatomic,strong) UIButton * cancelBtn;

@property (nonatomic,strong) UIButton * continueBtn;

// session
@property (nonatomic,strong) NSURLSession * session;

// Task
@property (nonatomic,strong) NSURLSessionDataTask * dataTask;

/**  下载进度条  */
@property (nonatomic,strong) UIProgressView * progressView;

/** 文件句柄 */
@property (nonatomic,strong) NSFileHandle * handle;
/** 下载总大小 */
@property (nonatomic,assign) NSInteger totalSize;

/** 当前下载大小 */
@property (nonatomic,assign) NSInteger currentSize;

/** 文件的全路径  */
@property (nonatomic,strong)  NSString * fullPath;

@end

@implementation ViewController

-(UIButton *)startBtn
{
    if (!_startBtn) {
        _startBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        
        _startBtn.frame = CGRectMake(SCREENWIDTH / 2 - 75, 80, 150, 30);
        [_startBtn setTitle:@"开始下载" forState:UIControlStateNormal];
        [_startBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [_startBtn setTitleColor:[UIColor blueColor] forState:UIControlStateSelected];
        [_startBtn addTarget:self action:@selector(clickStartBtn:) forControlEvents:UIControlEventTouchUpInside];
        
    }
    return _startBtn;
}

-(UIButton *)pauseBtn
{
    if (!_pauseBtn) {
        _pauseBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        
        _pauseBtn.frame = CGRectMake(SCREENWIDTH / 2 - 75, 130, 150, 30);
        [_pauseBtn setTitle:@"暂停下载" forState:UIControlStateNormal];
        [_pauseBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [_pauseBtn addTarget:self action:@selector(clickPauseBtn:) forControlEvents:UIControlEventTouchUpInside];
        
    }
    return _pauseBtn;
}

-(UIButton *)cancelBtn
{
    if (!_cancelBtn) {
        _cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        
        _cancelBtn.frame = CGRectMake(SCREENWIDTH / 2 - 75, 190, 150, 30);
        [_cancelBtn setTitle:@"取消下载" forState:UIControlStateNormal];
        [_cancelBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [_cancelBtn addTarget:self action:@selector(clickCancelBtn:) forControlEvents:UIControlEventTouchUpInside];
        
    }
    return _cancelBtn;
}

-(UIButton *)continueBtn
{
    if (!_continueBtn) {
        _continueBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        
        _continueBtn.frame = CGRectMake(SCREENWIDTH / 2 - 75, 250, 150, 30);
        [_continueBtn setTitle:@"继续下载" forState:UIControlStateNormal];
        [_continueBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [_continueBtn addTarget:self action:@selector(clickContinueBtn:) forControlEvents:UIControlEventTouchUpInside];
        
    }
    return _continueBtn;
}

-(NSString *)fullPath
{
    if (!_fullPath) {
        // 获得文件全路径
        _fullPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        // 拼接文件名称
        _fullPath = [self.fullPath stringByAppendingPathComponent:FileName];
    }
    return _fullPath;
}

-(NSURLSession *)session
{
    if (!_session) {
        // 3. 创建会话对象,设置代理
        /*
         第一参数: 配置信息  [NSURLSessionConfiguration defaultSessionConfiguration]  默认
         第二参数: 代理
         第三参数: 设置代理方法在哪个线程中调用
         */
        _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}

-(NSURLSessionDataTask *)dataTask
{
    if (_dataTask == nil) {
        
        // 1. 确定URL
        NSURL * url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_03.mp4"];
        
        // 2. 创建请求对象
        NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
        
        // 3 设置请求头信息,告诉服务器请求那一部分数据
        self.currentSize = [self getFileSize];
        
        NSString * range = [NSString stringWithFormat:@"bytes=%zd-",self.currentSize];
        [request setValue:range forHTTPHeaderField:@"Range"];
//        NSLog(@"请求体信息: %@",range);
        
        // 4. 创建Task
        _dataTask = [self.session dataTaskWithRequest:request];
        
    }
    return _dataTask;
}

#pragma mark - 获得指定文件路径对应文件的数据大小
-(NSInteger)getFileSize
{
    // 获得指定文件路径对应文件的数据大小
    NSDictionary * fileInfoDict = [[NSFileManager defaultManager] attributesOfItemAtPath:self.fullPath error:nil];
    NSLog(@"%@",fileInfoDict);
    
    NSInteger currentSize = [fileInfoDict[@"NSFileSize"] integerValue];
    return currentSize;
}

-(void)clickStartBtn:(UIButton *)btn
{
    [self download];
    
}

// 暂停,可以恢复
-(void)clickPauseBtn:(UIButton *)btn
{
    NSLog(@"------暂停------");
    [self.dataTask suspend];
}

// 该取消是不能恢复的
-(void)clickCancelBtn:(UIButton *)btn
{
    NSLog(@"------取消------");
    [self.dataTask cancel];
    
    self.dataTask = nil;
}


-(void)clickContinueBtn:(UIButton *)btn
{
    NSLog(@"------继续------");
    [self.dataTask resume];
}

- (UIProgressView *)progressView
{
    if (!_progressView) {
        _progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(SCREENWIDTH / 2 - 100, 50, 200, 30)];
        _progressView.progress = 0;
        
    }
    return _progressView;
}

-(void)download
{
//    // 1. 确定URL
//    NSURL * url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_03.mp4"];
//    
//    // 2. 创建请求对象
//    NSURLRequest * request = [NSURLRequest requestWithURL:url];
//    
//    // 3. 创建会话对象,设置代理
//    /*
//     第一参数: 配置信息  [NSURLSessionConfiguration defaultSessionConfiguration]  默认
//     第二参数: 代理
//     第三参数: 设置代理方法在哪个线程中调用
//     */
//    NSURLSession * session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//    
//    
//    // 4. 创建Task
//    NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:request];
    
    // 5. 执行Task
    [self.dataTask resume];
    
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    [self.view addSubview:self.startBtn];
    [self.view addSubview:self.pauseBtn];
    [self.view addSubview:self.cancelBtn];
    [self.view addSubview:self.continueBtn];
    [self.view addSubview:self.progressView];
    
    // 1. 读取保存的文件总数据大小,0
    NSInteger totalSize = [self getTotalSize];
    if (totalSize == 0) {
        self.progressView.progress = 0;
    }else{
        // 2. 获取当前已经下载的数据大小
        
        // 3. 计算的到进度信息
        self.progressView.progress = 1.0 * self.currentSize / totalSize;
    }
    
    NSLog(@"总大小为:%zd",totalSize);
    
    
}

#pragma mark - NSURLSessionDataDelegate
/**
 *  1. 接受到服务器的响应 它默认会取消该请求
 *
 *  @param session               会话对象
 *  @param dataTask              请求任务
 *  @param response              响应头信息
 *  @param completionHandler     回调  传给系统
 */
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    
    // 获得文件的总大小
    // expectedContentLength 本次请求的数据大小
    self.totalSize = response.expectedContentLength + self.currentSize;
    
    // 把数据总大小用NSUserDefaults纪录保存
    [self saveTotalSize:self.totalSize];
    
//    // 获得文件全路径
//    self.fullPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//    // 拼接文件名称
//    self.fullPath = [self.fullPath stringByAppendingPathComponent:response.suggestedFilename];
    
    if (self.currentSize == 0) {
        // 创建一个空的文件
        [[NSFileManager defaultManager] createFileAtPath:self.fullPath contents:nil attributes:nil];
        
    }
    
    // 创建文件句柄
    self.handle = [NSFileHandle fileHandleForWritingAtPath:self.fullPath];
    
    // 移动指针
    [self.handle seekToEndOfFile];
    
    /*
     NSURLSessionResponseCancel = 0,取消 默认
     NSURLSessionResponseAllow = 1, 接收
     NSURLSessionResponseBecomeDownload = 2, 变成下载任务
     NSURLSessionResponseBecomeStream        变成流
     */
    completionHandler(NSURLSessionResponseAllow);
}

/**
 *  接收到服务器返回的数据  调用多次
 *
 *  @param session          会话对象
 *  @param dataTask         请求任务
 *  @param data             本次下载的数据
 */
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
    
    
    // 写入数据到文件
    [self.handle writeData:data];
    
    // 当前下载的大小
    self.currentSize += data.length;
    
    CGFloat progressF = 1.0 * self.currentSize / self.totalSize;
    
    // 进度条显示进度状态
    self.progressView.progress = progressF;
    
    // 计算文件的下载进度
    NSLog(@"%f",1.0 * self.currentSize / self.totalSize);
    
}

/**
 *  请求结束或者是失败的时候调用
 *
 *  @param session          会话对象
 *  @param task             请求任务
 *  @param error            错误信息
 */
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSLog(@"下载好后的文件:%@",self.fullPath);
    
    // 关闭文件句柄
    [self.handle closeFile];
    self.handle = nil;
    
}

- (void)dealloc
{
    // 清理工作
    // finishTasksAndInvalidate
    [self.session invalidateAndCancel];
}


#pragma mark - 把数据总大小用NSUserDefaults纪录保存
-(void)saveTotalSize:(NSInteger)size
{
    NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
    
    [defaults setInteger:size forKey:TOTASLSIZE];
    
    [defaults synchronize];
}

#pragma mark - 获取下载数据总大小
-(NSInteger)getTotalSize
{
    NSInteger size = [[[NSUserDefaults standardUserDefaults] objectForKey:TOTASLSIZE] integerValue];
    return size;
}

@end

6. 掌握NSURLSession实现文件上传

#define SCREENWIDTH [UIScreen mainScreen].bounds.size.width
#define SCREENHEIGHT [UIScreen mainScreen].bounds.size.height

// 设置boundary的宏
#define Kboundary @"----WebKitFormBoundaryaItGoVAfuLHDoRD4"
#define KNewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]

#import "ViewController.h"

@interface ViewController ()<NSURLSessionDataDelegate>

@property (nonatomic,strong) UIButton * btn;

/**  进度条控件  */
@property (nonatomic,strong) UIProgressView * progressView;

@property (nonatomic,strong) NSURLSession * session;

@end

@implementation ViewController

-(UIButton *)btn
{
    if (!_btn) {
        _btn = [UIButton buttonWithType:UIButtonTypeCustom];
        
        _btn.frame = CGRectMake(SCREENWIDTH / 2 - 75, SCREENHEIGHT / 2 - 15, 150, 30);
        [_btn setTitle:@"上传" forState:UIControlStateNormal];
        [_btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [_btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
        
    }
    return _btn;
}

-(UIProgressView *)progressView
{
    if (_progressView == nil) {
        _progressView = [[UIProgressView alloc] init];
        _progressView.frame = CGRectMake(SCREENWIDTH / 2 - 100, 80, 200, 30);
        // 设置进度条进度
        _progressView.progress = 0;
    }
    return _progressView;
}

-(NSURLSession *)session
{
    if (_session == nil) {
        
        NSURLSessionConfiguration * config = [NSURLSessionConfiguration defaultSessionConfiguration];
        
        // 是否允许蜂窝访问
        config.allowsCellularAccess = YES;
        // 设置请求超时时间
        config.timeoutIntervalForRequest = 15;
        
        _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}

-(void)clickBtn:(UIButton *)btn
{
    
    [self upload];
    
//    [self upload2];
}


- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    [self.view addSubview:self.btn];
    [self.view addSubview:self.progressView];
    
}


-(void)upload
{
    // 1. 确定URL
    NSURL * url = [NSURL URLWithString:@"http://120.25.226.186:32812/upload"];
    
    // 2. 创建请求对象
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
    
    // 2.1 设置请求方法
    request.HTTPMethod = @"POST";
    
    // 2.2 设置请求头信息
     [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",Kboundary] forHTTPHeaderField:@"Content-Type"];
    
//    NSData * bodyData = [self getBodyData];
//    
//    // 2.3 设置请求体信息
//    request.HTTPBody = bodyData;
    
    // 3. 创建会话对象
//    NSURLSession * session = [NSURLSession sharedSession];
    
    // 4. 创建上传Task
    /*
     第一参数: 请求对象
     第二参数: 二进制数据,传递要上传的数据(请求体)
     第三参数: completionHandler 回调
            data: 响应体信息
            response: 响应头信息
            error: 错误信息
     */
    NSURLSessionUploadTask * uploadTask = [self.session uploadTaskWithRequest:request fromData:[self getBodyData] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        // 6. 解析
        NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        
    }];
    
    // 5. 执行task
    [uploadTask resume];
    
}

-(void)upload2
{
    // 1. 确定URL
    NSURL * url = [NSURL URLWithString:@"http://120.25.226.186:32812/upload"];
    
    // 2. 创建请求对象
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
    
    // 2.1 设置请求方法
    request.HTTPMethod = @"POST";
    
    // 2.2 设置请求头信息
    [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",Kboundary] forHTTPHeaderField:@"Content-Type"];
    
    //    NSData * bodyData = [self getBodyData];
    //
    //    // 2.3 设置请求体信息
    //    request.HTTPBody = bodyData;
    
    // 3. 创建会话对象
//    NSURLSession * session = [NSURLSession sharedSession];
//    NSURLSession * session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    
    // 4. 创建上传Task
    /*
     第一参数: 请求对象
     第二参数: 二进制数据,传递要上传的数据(请求体)
     第三参数: completionHandler 回调
     data: 响应体信息
     response: 响应头信息
     error: 错误信息
     */
    NSURLSessionUploadTask * uploadTask = [self.session uploadTaskWithRequest:request fromData:[self getBodyData] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        // 6. 解析
        NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        
    }];
    
    // 5. 执行task
    [uploadTask resume];
}

-(NSData *)getBodyData
{
    
    NSMutableData * fileData = [NSMutableData data];
    // 5.1 文件参数
    /*
     --分隔符
     Content-Disposition: form-data; name="file"; filename="静态05.jpg"
     Content-Type: image/jpeg(MIMEType:大类型/小类型)
     空行
     文件参数
     */
    
    NSData * oneData = [[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding];
    
    [fileData appendData:oneData];
    // 把换行添加进请求体数据中
    [fileData appendData:KNewLine];
    // name:file 服务器规定的参数
    // filename: 静态05.jpg 文件保存到服务器上面的名称
    // Content-Type: 文件的类型
    NSData * twoData = [@"Content-Disposition: form-data; name=\"file\"; filename=\"静态05.jpg\"" dataUsingEncoding:NSUTF8StringEncoding];
    
    [fileData appendData:twoData];
    [fileData appendData:KNewLine];
    
    NSData * thirdlyData = [@"Content-Type: image/jpeg" dataUsingEncoding:NSUTF8StringEncoding];
    [fileData appendData:thirdlyData];
    [fileData appendData:KNewLine];
    [fileData appendData:KNewLine];
    
    UIImage * image = [UIImage imageNamed:@"静态05"];
    // UIImage --> NSData
    NSData * imageData = UIImageJPEGRepresentation(image, 0.5);
    [fileData appendData:imageData];
    [fileData appendData:KNewLine];
    
    // 5.2 非文件参数
    /*
     --分隔符
     Content-Disposition: form-data; name="username"
     空行
     456123
     */
    NSData * sixthData = [[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding];
    
    [fileData appendData:sixthData];
    [fileData appendData:KNewLine];
    
    NSData * seventhData = [@"Content-Disposition: form-data; name=\"username\"" dataUsingEncoding:NSUTF8StringEncoding];
    [fileData appendData:seventhData];
    [fileData appendData:KNewLine];
    [fileData appendData:KNewLine];
    
    NSData * ninthData = [@"456123" dataUsingEncoding:NSUTF8StringEncoding];
    [fileData appendData:ninthData];
    [fileData appendData:KNewLine];
    
    
    // 5.3 结尾标识
    NSData * tenthData = [[NSString stringWithFormat:@"--%@--",Kboundary] dataUsingEncoding:NSUTF8StringEncoding];
    
    [fileData appendData:tenthData];
    
    return fileData;

}

#pragma mark - NSURLSessionDataDelegate
/**
 *  监控上传进度
 *
 *  @param session                           会话对象
 *  @param task                              请求任务
 *  @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
{
    NSLog(@"上传进度:%f",1.0 * totalBytesSent / totalBytesExpectedToSend);
    self.progressView.progress = 1.0 * totalBytesSent / totalBytesExpectedToSend;
}



@end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容