iOS NSURLSession

 

1 NSURLSession使用须知

NSURLSession的API是易步德

NSURLSession的使用分成系统代理方式和自定义方式

NSURLSession支持取消,恢复,挂起操作以及断掉续传的功能

NSURLSession的任务类型包括dataTask(以NSData对象进行上传和下载数据),downloadTask(以文件形式下载数据),uploadTask(以文件形式上传数据)

 

 1 NSURLSession使用的步骤

//1 请求的url
NSURL *url = [NSURL URLWithString:@"www.baidu.com/news/hfu3284"];

//2用url生成Request
NSURLRequest *request = [NSURLRequest requestWithURL:url];

//3 生成session,将request作为参数,调用session的dataWithRwquest来方式请求
NSURLSession *session = [NSURLSession sharedSession];

[[session dataTaskWithRequest:request] resume];

 

 2 http请求

//1、请求字符串
NSString *str = @"http://swiftcafe.io/2015/12/20/nsurlsession/";

//2、转化成url
NSURL *url = [NSURL URLWithString:[str stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];

//3、构建request
NSURLRequest *request = [NSURLRequest requestWithURL:url];

//4、获取session NSURLSession *session = [NSURLSession sharedSession]; //5、用session发送请求,返回一个dataTask NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (data && (error == nil)) { NSLog(@"data=%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); } else {
NSLog(
@"error=%@",error); } }]; //6、dataTask创建后是挂起状态,需要调用resume开启访问 [dataTask resume];

 

 3 文件下载

先创建一个NSURLSession类,再创建一个下载任务类NSURLSessionDownloadTask类,将session加入到下载任务中,最后开始下载任务.

下载任务开启后,NSURLSessionDownLoadTask默认会将数据写入本地沙河存储文件(tmp)中,所以我们需要在临时文件下载之后,即在NSURLSessionDownTask的completionHandler这个block中将临时文件剪切到一个永久的文件地址保存起来// 1、请求字符串NSString *str = [NSString stringWithFormat:@"http://swiftcafe.io/2015/12/20/nsurlsession/894.png"];

//2、转化成url
NSURL *url = [NSURL URLWithString:[str stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];

//3、构建request
NSURLRequest *request = [NSURLRequest requestWithURL:url];

//4、获取session
NSURLSession *session = [NSURLSession sharedSession];

//5、用session发送请求,返回一个downloadTask
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {

  // 文件将要移动到的指定目录
  NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];

  // 新文件路径
  NSString *newFilePath = [documentsPath stringByAppendingPathComponent:response.suggestedFilename]; 

  // 移动文件到新路径   [[NSFileManager defaultManager] moveItemAtPath:location.path toPath:newFilePath error:nil]; }]; //6、downloadTask创建后是挂起状态,需要调用resume开启访问 [downloadTask resume];

 

 

NSURLSession实现断点下载(不支持离线下载)

NSURLSession实现断点下载的步骤:(1)设置一个downloadTask、session以及resumeData的全局变量; (2)如果开始下载,就创建一个新的downloadTask,并启动下载; (3)如果暂停下载,调用取消下载的函数,并在block中保存本次的resumeData到全局resumeData中;(4)如果恢复下载,将上次保存的resumeData加入到任务中,并启动下载。

定义下载文件需要用到的类和要实现的代理

 

@interface ViewController () <NSURLSessionDownloadDelegate>
/** 下载进度条 */
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;

/** 下载进度条Label */
@property (weak, nonatomic) IBOutlet UILabel *progressLabel;

/** NSURLSession断点下载(不支持离线)需用到的属性 **********/

/** 下载任务 */ @property (nonatomic, strong) NSURLSessionDownloadTask *downloadTask; /** 保存上次的下载信息 */ @property (nonatomic, strong) NSData *resumeData; /** session */ @property (nonatomic, strong) NSURLSession *session; @end

 

实现下面的按钮点击代码,其中用到了session的懒加载。

/**
 * 点击按钮 -- 使用NSURLSession断点下载(不支持离线)
 */
- (IBAction)resumeDownloadBtnClicked:(UIButton *)sender {
    // 按钮状态取反
    sender.selected = !sender.isSelected;
    if (nil == self.downloadTask) { // [开始下载/继续下载]
        if (self.resumeData) { // [继续下载]
            // 传入上次暂停下载返回的数据,就可以恢复下载
            self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];
            // 开始任务
            [self.downloadTask resume];
            self.resumeData = nil;
        }else{ // [开始下载]:从0开始下载
            NSURL* url = [NSURL URLWithString:@"http://dldir1.qq.com/qqfile/QQforMac/QQ_V5.4.0.dmg"];
            // 创建任务
            self.downloadTask = [self.session downloadTaskWithURL:url];
            // 开始任务
            [self.downloadTask resume];
        }
    }else{ // [暂停下载]

        __weak typeof(self) weakSelf = self;

        [self.downloadTask cancelByProducingResumeData:^(NSData *resumeData) {

            // resumeData:包含了继续下载的位置\下载的路径

            weakSelf.resumeData = resumeData;

            weakSelf.downloadTask = nil;

        }];
    }
}

 

 

NSURLSession实现断点下载(支持离线下载) :用NSURLSessionDataTask来实现NSURLSession的离线断点下载。

NSURLSessionDataTask在发送请求之后,能够将返回的数据,作为data一部分一部分的接受过来。这样,我们就可以像NSURLConnection上边那样,创建一个NSFilehandle(文件句柄)类,在接受数据的时候,一点点写入永久沙盒文件中。并且在下次开始的时候,设置好HTTP请求头的Rang。我们就可以实现离线断点下载了。

 

定义下载文件需要用到的类和要实现的代理

@interface ViewController () <NSURLSessionDataDelegate>

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

/** 下载进度条Label */

@property (weak, nonatomic) IBOutlet UILabel *progressLabel;

 

/** NSURLSession断点下载(支持离线)需用到的属性 **********/

/** 文件的总长度 */

@property (nonatomic, assign) NSInteger fileLength;

/** 当前下载长度 */

@property (nonatomic, assign) NSInteger currentLength;

/** 文件句柄对象 */

@property (nonatomic, strong) NSFileHandle *fileHandle;

 

/** 下载任务 */

@property (nonatomic, strong) NSURLSessionDataTask *downloadTask;

/** session */

@property (nonatomic, strong) NSURLSession *session;

 

@end

 

添加支持断点下载的[开始下载/暂停下载]按钮,并实现相应功能的代码

/**
session的懒加载
 */

- (NSURLSession *)session
{
    if (!_session) {
        _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}


/**
 downloadTask的懒加载,这里设置请求头中的Range
 */
- (NSURLSessionDataTask *)downloadTask {
   if (!_downloadTask) {
       // 创建下载URL
        NSURL *url = [NSURL URLWithString:@"http://dldir1.qq.com/qqfile/QQforMac/QQ_V5.4.0.dmg"];

        // 2.创建request请求
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

        // 设置HTTP请求头中的Range
        NSString *range = [NSString stringWithFormat:@"bytes=%zd-", self.currentLength];
        [request setValue:range forHTTPHeaderField:@"Range"];

        // 3. 下载
        _downloadTask = [self.session dataTaskWithRequest:request];
    }
    return _downloadTask;
}


/**
 点击按钮 -- 使用NSURLSession断点下载(支持离线)
 */
- (IBAction)OfflinResumeDownloadBtnClicked:(UIButton *)sender {
    // 按钮状态取反
    sender.selected = !sender.isSelected;

    if (sender.selected) { // [开始下载/继续下载]
        // 沙盒文件路径
        NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"QQ_V5.4.0.dmg"];

        NSInteger currentLength = [self fileLengthForPath:path];
        if (currentLength > 0) {  // [继续下载]
            self.currentLength = currentLength;
        }
         [self.downloadTask resume];

    } else {
        [self.downloadTask suspend];
        self.downloadTask = nil;
    }
}

/**

 * 获取已下载的文件大小

 */

- (NSInteger)fileLengthForPath:(NSString *)path {

    NSInteger fileLength = 0;

    NSFileManager *fileManager = [[NSFileManager alloc] init]; // default is not thread safe

    if ([fileManager fileExistsAtPath:path]) {

        NSError *error = nil;

        NSDictionary *fileDict = [fileManager attributesOfItemAtPath:path error:&error];

        if (!error && fileDict) {

            fileLength = [fileDict fileSize];

        }

    }

    return fileLength;

}

 

 

 

4 文件上传:文件上传则需要用到POST方法,套路其实都很接近,只不过会有一个formData参数需要传入,这个参数就是一些上传文件的NSData

// 1、请求字符串

NSString *str = [NSString stringWithFormat:@"http://swiftcafe.io/2015/12/20/nsurlsession/thumbnail.php"];

//2、转化成url

NSURL *url = [NSURL URLWithString:[str stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];

//3、构建request

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

request.HTTPMethod = @"POST";

//4、获取session

NSURLSession *session = [NSURLSession sharedSession];

 

//5、用session发送请求,返回一个uploadTask

NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:[NSData dataWithContentsOfFile:@"IMG_0359.jpg"]     completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

    if (error == nil) {

        NSLog(@"upload success:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);

    } else {

        NSLog(@"upload error:%@",error);

    }

}];

//6、开始上传

[uploadTask resume];

最后实现相关的NSURLSessionDataDelegate方法

#pragma mark - <NSURLSessionDataDelegate> 实现方法

/**

 * 接收到响应的时候:创建一个空的沙盒文件

 */

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler

{

    // 获得下载文件的总长度:请求下载的文件长度 + 当前已经下载的文件长度

    self.fileLength = response.expectedContentLength + self.currentLength;

 

    // 沙盒文件路径

    NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"QQ_V5.4.0.dmg"];

 

    NSLog(@"File downloaded to: %@",path);

 

    // 创建一个空的文件到沙盒中

    NSFileManager *manager = [NSFileManager defaultManager];

 

    if (![manager fileExistsAtPath:path]) {

        // 如果没有下载文件的话,就创建一个文件。如果有下载文件的话,则不用重新创建(不然会覆盖掉之前的文件)

        [manager createFileAtPath:path contents:nil attributes:nil];

    }

 

    // 创建文件句柄

    self.fileHandle = [NSFileHandle fileHandleForWritingAtPath:path];

 

    // 允许处理服务器的响应,才会继续接收服务器返回的数据

    completionHandler(NSURLSessionResponseAllow);

}

/**
 * 接收到具体数据:把数据写入沙盒文件中
 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
    // 指定数据的写入位置 -- 文件内容的最后面
    [self.fileHandle seekToEndOfFile];
    // 向沙盒写入数据
    [self.fileHandle writeData:data];
    // 拼接文件总长度
    self.currentLength += data.length;
    NSLog(@"%ld",self.currentLength);
    __weak typeof(self) weakSelf = self;
    // 获取主线程,不然无法正确显示进度。
    NSOperationQueue* mainQueue = [NSOperationQueue mainQueue];
    [mainQueue addOperationWithBlock:^{
        // 下载进度
        weakSelf.progressView.progress =  1.0 * weakSelf.currentLength / weakSelf.fileLength;
        weakSelf.progressLabel.text = [NSString stringWithFormat:@"当前下载进度:%.2f%%",100.0 * self.currentLength / self.fileLength];
    }];
}
/**
 *  下载完文件之后调用:关闭文件、清空长度
 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    // 关闭fileHandle
    [self.fileHandle closeFile];
    self.fileHandle = nil;
    // 清空长度
    self.currentLength = 0;
    self.fileLength = 0;
}

 

posted @ 2017-04-10 12:25  MK_monster  阅读(138)  评论(0编辑  收藏  举报