美文网首页iOS基本功iOS面试总结ios网络编程
iOS-NSURLSession详解(附实战代码)

iOS-NSURLSession详解(附实战代码)

作者: 路飞_Luck | 来源:发表于2019-04-04 00:04 被阅读71次
    目录
    • 创建NSURLSession对象
    • NSURLSessionConfiguration详解
    • URLSessionTask详解
      • NSURLSessionDataTask详解
      • NSURLSessionDownloadTask详解
      • NSURLSessionUploadTask详解
    • 断点续传
    序言

    在iOS9.0之后,以前使用的NSURLConnection过期,苹果推荐使用NSURLSession来替换NSURLConnection完成网路请求相关操作。NSURLSession的使用非常简单,只需要三个步骤。

    • 1.创建NSURLSession对象
    • 2.通过 NSURLSession 的实例创建 Task
    • 3.执行 Task
    一 创建NSURLSession对象
    • 通过单例获取
    @property (class, readonly, strong) NSURLSession *sharedSession;
    
    • 通过工厂方法获取
    // 不用代理
    + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
    
    // 使用代理
    + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id <NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue;
    
    

    实例代码

    // 创建 session 对象
    - (void)createSession {
        // 1.单例
        NSURLSession *session = [NSURLSession sharedSession];
    
        // 2.工厂方法 - 不使用代理
        NSURLSession *session1 = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    
        // 3.工厂方法 - 使用代理
        NSURLSession *session2 = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
                                                               delegate:self
                                                          delegateQueue:[NSOperationQueue mainQueue]];
    }
    

    在使用自定义方式创建NSURLSession对像时,都需要传入一个NSURLSessionConfiguration参数,这个参数是对Session的网络请求的基本配置。那这个NSURLSessionConfiguration都有哪些配置呢?

    二 NSURLSessionConfiguration详解

    有三个方法来创建NSURLSessionConfiguration:

    @property (class, readonly, strong) NSURLSessionConfiguration *defaultSessionConfiguration;
    @property (class, readonly, strong) NSURLSessionConfiguration *ephemeralSessionConfiguration;
    
    // macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)
    + (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier;
    

    解释说明

    • defaultSessionConfiguration 使用全局的cache,cookie,使用硬盘来缓存数据
    • ephemeralSessionConfiguration 临时session配置,与默认配置相比,这个配置不会将缓存、cookie等存在本地,只会存在内存里,所以当程序退出时,所有的数据都会消失
    • backgroundSessionConfigurationWithIdentifier:后台session配置,与默认配置类似,不同的是会在后台开启另一个线程来处理网络数据

    一旦创建了NSURLSessionConfiguration就可以给它设置各种属性

    下面对NSURLSessionConfiguration相关属性介绍 - 摘抄别人的

    @interface NSURLSessionConfiguration : NSObject <NSCopying>
    
    /* 三种创建方式 */
    
    + (NSURLSessionConfiguration *)defaultSessionConfiguration;
    + (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
    + (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier NS_AVAILABLE(10_10, 8_0);
    
    /* 当使用上述第三种方式创建后台sessionConfiguration时可以读到初始化时传入的唯一标识,其他创建方式都为空 */
    @property (nullable, readonly, copy) NSString *identifier;
    
    /* 
    缓存策略,默认值是NSURLRequestUseProtocolCachePolicy
     */
    @property NSURLRequestCachePolicy requestCachePolicy;
    
    /* 给request指定每次接收数据超时间隔,如果下一次接受新数据用时超过该值,则发送一个请求超时给该request。默认为60s */
    @property NSTimeInterval timeoutIntervalForRequest;
    
    /* 给指定resource设定一个超时时间,resource需要在时间到达之前完成。默认是7天。 */
    @property NSTimeInterval timeoutIntervalForResource;
    
    /* 指定网络传输类型。精切指出传输类型,可以让系统快速响应,提高传输质量,延长电池寿命等。
    typedef NS_ENUM(NSUInteger, NSURLRequestNetworkServiceType)
    {
        NSURLNetworkServiceTypeDefault = 0, // 普通网络传输,默认使用这个
        NSURLNetworkServiceTypeVoIP = 1,    // 网络语音通信传输,只能在VoIP使用
        NSURLNetworkServiceTypeVideo = 2,   // 影像传输
        NSURLNetworkServiceTypeBackground = 3, // 网络后台传输,优先级不高时可使用。对用户不需要的网络操作可使用
        NSURLNetworkServiceTypeVoice = 4       // 语音传输
    };
     */
    @property NSURLRequestNetworkServiceType networkServiceType;
    
    /* 是否使用蜂窝网络,默认是yes. */
    @property BOOL allowsCellularAccess;
    
    /* 是否由系统根据性能自动裁量后台任务。默认值是NO。同sessionSendsLaunchEvent一样,只对后台configuration有效。 */
    @property (getter=isDiscretionary) BOOL discretionary NS_AVAILABLE(10_10, 7_0);
    
    /* 
    如果要为app的插件提供session,需要给这个值赋值
     */
    @property (nullable, copy) NSString *sharedContainerIdentifier NS_AVAILABLE(10_10, 8_0);
    
    /* 
     表示当后台传输结束时,是否启动app.这个属性只对 后台sessionConfiguration 生效,其他configuration类型会自动忽略该值。默认值是YES。
     */
    @property BOOL sessionSendsLaunchEvents NS_AVAILABLE(NA, 7_0);
    
    /* 
    指定了会话连接中的代理服务器。同样地,大多数面向消费者的应用程序都不需要代理,所以基本上不需要配置这个属性,默认为NULL
    */
    @property (nullable, copy) NSDictionary *connectionProxyDictionary;
    
    /* 确定是否支持SSLProtocol版本的会话
     */
    @property SSLProtocol TLSMinimumSupportedProtocol;
    
    /* 
    确定是否支持SSLProtocol版本的会话
    */
    @property SSLProtocol TLSMaximumSupportedProtocol;
    
    /* 
    它可以被用于开启HTTP管道,这可以显着降低请求的加载时间,但是由于没有被服务器广泛支持,默认是禁用的
     */
    @property BOOL HTTPShouldUsePipelining;
    
    /* 
    默认为yes,是否提供来自shareCookieStorge的cookie,如果想要自己提供cookie,可以使用HTTPAdditionalHeaders来提供。
     */
    @property BOOL HTTPShouldSetCookies;
    
    /* Policy for accepting cookies.  This overrides the policy otherwise specified by the cookie storage. */
    @property NSHTTPCookieAcceptPolicy HTTPCookieAcceptPolicy;
    
    /* 
    指定了一组默认的可以设置出站请求的数据头。这对于跨会话共享信息,如内容类型,语言,用户代理,身份认证,是很有用的。
    例如:
        @{@"Accept": @"application/json",
         @"Accept-Language": @"en",
         @"Authorization": authString,
         @"User-Agent": userAgentString
       }
     */
    @property (nullable, copy) NSDictionary *HTTPAdditionalHeaders;
    
    /* 
    同时连接一个host的最大数。iOS默认是4.APP是作为一个整体来看的
     */
    @property NSInteger HTTPMaximumConnectionsPerHost;
    
    /* 
    存储cookie,清除存储,直接set为nil即可。
    对于默认和后台的session,使用sharedHTTPCookieStorage。
    对于短暂的session,cookie仅仅储存到内存,session失效时会自动清除。
     */
    @property (nullable, retain) NSHTTPCookieStorage *HTTPCookieStorage;
    
    /* 
    证书存储,如果不使用,可set为nil.
    默认和后台session,默认使用的sharedCredentialStorage.
    短暂的session使用一个私有存储在内存中。session失效会自动清除。
     */
    @property (nullable, retain) NSURLCredentialStorage *URLCredentialStorage;
    
    /* 
    缓存NSURLRequest的response。
    默认的configuration,默认值的是sharedURLCache。
    后台的configuration,默认值是nil
    短暂的configuration,默认一个私有的cache于内存,session失效,cache自动清除。
    */
    @property (nullable, retain) NSURLCache *URLCache;
    
    /* Enable extended background idle mode for any tcp sockets created.    Enabling this mode asks the system to keep the socket open
     *  and delay reclaiming it when the process moves to the background (see https://developer.apple.com/library/ios/technotes/tn2277/_index.html) 
     */
    @property BOOL shouldUseExtendedBackgroundIdleMode NS_AVAILABLE(10_11, 9_0);
    
    /* 
    处理NSURLRequest的NSURLProtocol的子类。
    重要:对后台Session失效。
     */
    @property (nullable, copy) NSArray<Class> *protocolClasses;
    
    @end
    

    掌握NSURLSessionConfiguration参数,那我们就可以创建一个session对象,然后通过它发送网络请求了。

    三 URLSessionTask详解

    NSURLSessionTask是一个抽象类,其下有4个实体子类可以直接使用

    • NSURLSessionDataTask
    • NSURLSessionUploadTask
    • NSURLSessionDownloadTask
    • NSURLSessionStreamTask

    这四个子类封装了现代程序四个最基本的网络任务:获取数据,比如JSON或者XML,上传文件和下载文件还有数据流的获取。

    看看类的声明

    • NSURLSessionDataTask
    // NSURLSessionDataTask
    @interface NSURLSessionDataTask : NSURLSessionTask
    @end
    
    • NSURLSessionUploadTask
    // NSURLSessionUploadTask
    @interface NSURLSessionUploadTask : NSURLSessionDataTask
    @end
    
    • NSURLSessionDownloadTask
    // NSURLSessionDownloadTask
    @interface NSURLSessionDownloadTask : NSURLSessionTask
    - (void)cancelByProducingResumeData:(void (^)(NSData * _Nullable resumeData))completionHandler;
    @end
    
    • NSURLSessionStreamTask
    @interface NSURLSessionStreamTask : NSURLSessionTask
    - (void)readDataOfMinLength:(NSUInteger)minBytes maxLength:(NSUInteger)maxBytes timeout:(NSTimeInterval)timeout completionHandler:(void (^) (NSData * _Nullable data, BOOL atEOF, NSError * _Nullable error))completionHandler;
    
    - (void)writeData:(NSData *)data timeout:(NSTimeInterval)timeout completionHandler:(void (^) (NSError * _Nullable error))completionHandler;
    
    - (void)captureStreams;
    
    - (void)closeWrite;
    
    - (void)closeRead;
    
    - (void)startSecureConnection;
    
    - (void)stopSecureConnection;
    
    @end
    

    下面是一幅总结图

    NSURLSessionTask.png

    NSURLSession比NSURLConnection最方便的地方就是任务可以暂停继续。在网络请求中,真正去执行下载或者上传任务的就是URLSessionTask,我们来看一下它常用的方法:

    • - (void)resume; 当使用NSURLSession创建一个NSURLSessionTask任务时,要手动调用此方法,任务才会开启,而NSURLConnection默认开启。

    • - (void)suspend;暂停任务方法,手动调用会暂停。当前任务,再次开启此任务时,会从紧接上次任务开始,会面会说到如何暂停任务再开启任务。

    • - (void)cancel;取消任务。

    NSURLSessionTask还有一个属性

    // The current state of the task within the session.
    @property (readonly) NSURLSessionTaskState state;
    
    typedef NS_ENUM(NSInteger, NSURLSessionTaskState) {
        NSURLSessionTaskStateRunning = 0,  /* 正在执行 The task is currently being serviced by the session */
        NSURLSessionTaskStateSuspended = 1,  // 暂停状态
        NSURLSessionTaskStateCanceling = 2,  /* 取消状态 The task has been told to cancel.  The session will receive a URLSession:task:didCompleteWithError: message. */
        NSURLSessionTaskStateCompleted = 3,  /* 任务完成状态 The task has completed and the session will receive no more delegate notifications */
    } NS_ENUM_AVAILABLE(NSURLSESSION_AVAILABLE, 7_0);
    

    上面说到的四个类,都直接或间接继承NSURLSessionTask,所有NSURLSessionTask的方法或者属性这四个类都有,接下来详细介绍这四个类。

    3.1 NSURLSessionDataTask

    NSURLSessionDataTask是开发中使用频率最高的,我们平常使用的GET和POST请求都是通过它来实现的,如果请求的数据简单并且不需要对获取的数据进行复杂操作,我们使用 Block 解析返回的数据即可。具体代码如下:

    GET 请求

    - (void)sessionDataTaskGet {
        // 1.请求路径
        NSURL *url = [NSURL URLWithString:@"http://rap2api.taobao.org/app/mock/163155/gaoshilist"];
        // 2.创建请求对象
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        // 3.创建 session 对象
        NSURLSession *session = [NSURLSession sharedSession];
        
        // 4.普通任务 - get
        NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse * response, NSError *error) {
            if (error) {
                NSLog(@"NSURLSessionDataTaskerror:%@",error);
                return;
            }
            
            //5.解析数据
            NSLog(@"NSURLSessionDataTask:%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
        }];
        
        // 启动任务
        [dataTask resume];
    }
    

    运行结果

    NSURLSessionDataTask-Get.png

    POST请求

    - (void)sessionDataTaskPost {
        // 1.请求路径
        NSURL *url = [NSURL URLWithString:@"http://rap2api.taobao.org/app/mock/163155/fankui"];
        // 2.创建请求对象
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        // 设置 post 请求方式
        request.HTTPMethod = @"POST";
        // 设置请求体
        request.HTTPBody = [@"username=1234&pwd=4321" dataUsingEncoding:NSUTF8StringEncoding];
       
        NSURLSession *session = [NSURLSession sharedSession];
        
        NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            if (error) {
                NSLog(@"NSURLSessionDataTaskerror:%@",error);
                return;
            }
            
            //5.解析数据
            NSLog(@"NSURLSessionDataTask:%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
        }];
        
        [dataTask resume];
    }
    

    运行结果

    NSURLSessionDataTask-post.png

    代理实现
    另外我们也可以设置session的代理来实时的监听数据

    + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id <NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue;
    
    

    它有4个直接或间接的子协议

    • NSURLSessionTaskDelegate
    • NSURLSessionDownloadDelegate
    • NSURLSessionStreamDelegate
    • NSURLSessionDataDelegate

    实例代码如下

    - (void)sessionDataTaskPostDelegate {
        // 1.请求路径
        NSURL *url = [NSURL URLWithString:@"http://rap2api.taobao.org/app/mock/163155/fankui"];
        // 2.创建请求对象
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        // 设置 post 请求方式
        request.HTTPMethod = @"POST";
        // 设置请求体
        request.HTTPBody = [@"username=1234&pwd=4321" dataUsingEncoding:NSUTF8StringEncoding];
        
        NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
        
        NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
        
        [dataTask resume];
    }
    

    NSURLSessionDataDelegate协议方法

    // 1.接收到服务器的响应
    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveResponse:(NSURLResponse *)response
     completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
        NSLog(@"didReceiveResponse");
        // 必须设置对响应进行允许处理才会执行后面两个操作。
        completionHandler(NSURLSessionResponseAllow);
    }
    
    // 2.接收到服务器的数据(可能调用多次)
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
        NSLog(@"接受到服务器的数据:%lu",data.length);
    }
    
    // 3.请求成功或者失败(如果失败,error有值)
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
        if (error) {
            NSLog(@"请求失败: %@",error.description);
        } else {
            NSLog(@"请求成功");
        }
    }
    

    运行结果如下

    NSURLSessionDataDelegate.png
    3.2 NSURLSessionDownloadTask

    NSURLSessionDownloadTask在下载文件的时候,是将数据一点点地写入本地的临时文件。所以在 completionHandler 这个 block 里,我们需要把文件从一个临时地址移动到一个永久的地址保存起来:

    • 实例代码
    - (void)sessionDownloadTask {
        // 1.请求路径
        NSURL *url = [NSURL URLWithString:@"http://www.pptbz.com/pptpic/UploadFiles_6909/201203/2012031220134655.jpg"];
        // 2.创建 session 对象
        NSURLSession *session = [NSURLSession sharedSession];
        
        // 下载 task
        NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
            // 获取沙盒的 caches 路径
            NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];
            // 生成 url 路径
            NSURL *url = [NSURL fileURLWithPath:path];
            // 将文件保存到指定文件目录下
            [[NSFileManager defaultManager] moveItemAtURL:location toURL:url error:nil];
            NSLog(@"path = %@",path);
            NSLog(@"%@",[NSThread currentThread]);
            //切记当前为子线程,
            dispatch_async(dispatch_get_main_queue(), ^{
                self.imgView.image = [UIImage imageNamed:path];
            });
        }];
        
        [task resume];
    }
    

    运行结果

    image.png image.png

    通过代理方法下载

    #pragma mark - sessionDownloadTaskDelegate
    
    - (void)sessionDownloadTaskDelegate {
        // 1.请求路径
    //    NSURL *url = [NSURL URLWithString:@"http://www.pptbz.com/pptpic/UploadFiles_6909/201203/2012031220134655.jpg"];
        NSURL *url = [NSURL URLWithString:@"http://dldir1.qq.com/qqfile/QQforMac/QQ_V5.4.0.dmg"];
        // 2.创建带有代理方法的自定义 session
        NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
        // 3.创建任务
        NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url];
        // 4. 开启任务
        [task resume];
    }
    
    #pragma mark - NSURLSessionDownloadDelegate
    
    /**
     *  写入临时文件时调用
     *  @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 {
        //可以监听下载的进度
        CGFloat progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite;
        NSLog(@"downloadTask %f",progress);
    }
    
    // 下载完成调用
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
        // location 还是一个临时路径,需要自己挪到需要的路径(caches 文件夹)
        NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
        [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:filePath] error:nil];
        NSLog(@"downloadTask 移动文件路径");
    }
    

    执行结果

    image.png image.png image.png
    3.3 断点续传

    在开发中,我们经常由于某种原因,在下载或上传的时候往往不能一次性下载或上传完,有可能下载或上传了一半就终止了,这时候当条件满足继续下载或上传时,我们不希望从头开始,这时候就可以使用断点续传。它的大概思路是:

    • 某种限制,续传暂停

    • 将暂停后数据(当前数据)保存起来--_resumeData = resumeData;

    • 条件允许续传时,使用resumeData创建新的NSURLSessionTask

    • 实例代码如下

    /** 进度条*/
    @property(nonatomic,strong)UIView *progressView;
    /** 进度值*/
    @property(nonatomic,strong)UILabel *progressLbe;
    /** downloadtask*/
    @property(nonatomic,strong)NSURLSessionDownloadTask *downloadTask;
    /** data*/
    @property(nonatomic,strong)NSData *resumeData;
    /** session*/
    @property(nonatomic,strong)NSURLSession *session;
    
    • 下载和取消按钮操作
    #pragma mark - 按钮点击
    
    // 开始下载
    - (void)tapDownload {
        // 1.URL
        NSURL *url = [NSURL URLWithString:@"http://dldir1.qq.com/qqfile/QQforMac/QQ_V5.4.0.dmg"];
        if (self.resumeData) {  // 之前已经下载过了
            self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];
        } else {
            self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
            NSURLRequest *request = [NSURLRequest requestWithURL:url];
            self.downloadTask = [self.session downloadTaskWithRequest:request];
        }
        
        [self.downloadTask resume];
    }
    
    // 暂停下载
    - (void)tapSuspend {
        if (self.downloadTask) {
            __weak typeof (self)weakSelf = self;
            [self.downloadTask cancelByProducingResumeData:^(NSData *resumeData) {
                NSLog(@"resumeData:%@",resumeData);
                weakSelf.resumeData = resumeData;
                weakSelf.downloadTask = nil;
            }];
        }
    }
    
    // 恢复
    - (void)tapRecover {
        self.downloadTask = nil;
        self.session = nil;
        self.resumeData = nil;
        self.progressLbe.text = @"0";
        CGRect frame = CGRectMake(0, 0, kScreanWidth * 0.6 * 0, 20);
        self.progressView.frame = frame;
    }
    
    
    • NSURLSessionDownloadDelegate
    #pragma mark - NSURLSessionDownloadDelegate
    
    /**
     *  写入临时文件时调用
     *  @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 {
        //可以监听下载的进度
        CGFloat progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite;
        dispatch_async(dispatch_get_main_queue(), ^{
            self.progressLbe.text = [NSString stringWithFormat:@"%.2f",progress];
            CGRect frame = CGRectMake(0, 0, kScreanWidth * 0.6 * progress, 20);
            self.progressView.frame = frame;
        });
    }
    
    // 下载完成调用
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
        // location 还是一个临时路径,需要自己挪到需要的路径(caches 文件夹)
        NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
        [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:filePath] error:nil];
        NSLog(@"downloadTask 移动文件路径");
    }
    

    运行结果

    断点下载.gif
    3.4 NSURLSessionUploadTask

    在 NSURLSession 中,文件上传主要使用五种方式:

    - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;
    
    - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;
    
    - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;
    
    - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;
    
    - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;
    

    下面我们介绍使用uploadTaskWithRequest:fromData表单的形式上传数据

    • 发送请求
    // 发送请求
    - (void)sessionUploadTask {
        NSURL *url = [NSURL URLWithString:@"https://api.weibo.com/2/statuses/public_timeline.json"];
        // 请求
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        // 设置一些参数
        [request setHTTPMethod:@"POST"];
        //设置请求体
        [request setValue:[NSString stringWithFormat: @"multipart/form-data;%@", @"cs"] forHTTPHeaderField:@"Content-type"];
        //获取上传的图片的data
        NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"picture" ofType:@"jpg"]];
        //此处添加需要看清楚内容
        NSData *body =  [self httpFormDataBodyWithBoundary:@"cs" params:@{@"access_token":@"2.00cYYKWF6EKpiB3883361b1dJiZ4eD",@"status":@"哈哈,这是我测试NSURLSession上传文件的微博"} fieldName:@"pic" fileName:@"pic.png" fileContentType:@"image/png" data:data];
        // 发送请求
        NSURLSession *session = [NSURLSession sharedSession];
        NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            if (error) {
                NSLog(@"error = %@",error.description);
            } else {
                NSLog(@"upload  success");
            }
        }];
        [uploadTask resume];
    }
    
    • 拼接请求体
    - (NSData *)httpFormDataBodyWithBoundary:(NSString *)boundary
                                      params:(NSDictionary *)params
                                   fieldName:(NSString *)fieldName
                                    fileName:(NSString *)fileName
                             fileContentType:(NSString *)fileContentType
                                        data:(NSData *)fileData {
        
        NSString *preBoundary = [NSString stringWithFormat:@"--%@",boundary];
        NSString *endBoundary = [NSString stringWithFormat:@"--%@--",boundary];
        NSMutableString *body = [[NSMutableString alloc] init];
        //遍历
        for (NSString *key in params) {
            //得到当前的key
            //如果key不是当前的pic,说明value是字符类型,比如name:Boris
            //添加分界线,换行,必须使用\r\n
            [body appendFormat:@"%@\r\n",preBoundary];
            //添加字段名称换2行
            [body appendFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",key];
            //添加字段值
            [body appendFormat:@"%@\r\n",[params objectForKey:key]];
        }
        //添加分界线,换行
        [body appendFormat:@"%@\r\n",preBoundary];
        //声明pic字段,文件名为boris.png
        [body appendFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n",fieldName,fileName];
        //声明上传文件的格式
        [body appendFormat:@"Content-Type: %@\r\n\r\n",fileContentType];
        //声明结束符
        NSString *endStr = [NSString stringWithFormat:@"\r\n%@",endBoundary];
        //声明myRequestData,用来放入http  body
        NSMutableData *myRequestData = [NSMutableData data];
        //将body字符串转化为UTF8格式的二进制
        [myRequestData appendData:[body dataUsingEncoding:NSUTF8StringEncoding]];
        //将image的data加入
        [myRequestData appendData:fileData];
        //加入结束符--hwg--
        [myRequestData appendData:[endStr dataUsingEncoding:NSUTF8StringEncoding]];
        return myRequestData;
    }
    

    执行结果

    image.png

    这里我们需要拼接一个表单数据,才能够上传数据。 当然,我们也可以用代理方法来监听上传的进度。


    本文参考
    NSURLSession与NSURLConnection区别


    项目连接地址 - NSURLSessionDemo


    更多相关文章参考
    iOS-NSURLSession与NSURLConnection区别
    iOS-NSURLConnection使用详解

    相关文章

      网友评论

        本文标题:iOS-NSURLSession详解(附实战代码)

        本文链接:https://www.haomeiwen.com/subject/nyoobqtx.html