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

目录
  • 创建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使用详解

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

推荐阅读更多精彩内容

  • NSURLSession概述1. NSURLSession session类型NSURLSession包括下面3种...
    瞎嘚嘚阅读 1,882评论 2 2
  • NSUrlSession专题 1概述 【主要参考】NSURLSession http://blog.csdn.ne...
    Kevin_Junbaozi阅读 4,157评论 1 15
  • 在苹果彻底弃用NSURLConnection之后自己总结的一个网上的内容,加上自己写的小Demo,很多都是借鉴网络...
    付寒宇阅读 4,280评论 2 13
  • 春暖花开,登上幻想的高山 微风绕指过,阳光攀上衫 飞花为你做只点缀,发丝牵扯着风 你的皮肤发着柔和的微光 你是在望...
    不期待过去的猪阅读 266评论 0 0
  • 当你不再需要吸引任何东西的时候,你原来需要的人事物反而倒过来需要你。 唯有你不设限,圆梦的方法和结果,创造力法则才...
    王静_06c7阅读 158评论 0 0