代码相关

NSURLSession与NSURLConnection区别

timhbw · 5月2日 · 2017年 784次已读

今天去面试,被问了一道AFNetworking2.0和3.0有什么区别,当时心想,这谁不知道啊,随口答到:2.0使用的NSURLConnection,3.0使用的是NSURLSession,人家又问NSURLSesstion和NSURLConnection有什么区别,瞬间傻眼了,支支吾吾只是说出了缓存策略的不同。亡羊补牢,为时未晚,这篇博客就讲解一下它们两者到底有什么区别。

 

使用现状

NSURLSession是NSURLConnection的替代者,在2013年苹果全球开发者大会上(WWDC2013)随iOS7一起发布的,是对NSURLConnection进行了重构优化后的新的网络接口。从iOS9开始,NSURLConnection中发送请求的两个方法已经过期(同步请求,异步请求),初始化网络连接的方法也被设置为过期,系统不再推荐使用,建议使用NSURLSession发送网络请求。NSURLConnection被废弃的主要接口:

-(nullableinstancetype)initWithRequest:(NSURLRequest*)requestdelegate:(nullableid)delegatestartImmediately:(BOOL)startImmediatelyNS_DEPRECATED(10_5,10_11,2_0,9_0,"UseNSURLSession(seeNSURLSession.h)")__WATCHOS_PROHIBITED;


-(nullableinstancetype)initWithRequest:(NSURLRequest*)requestdelegate:(nullableid)delegateNS_DEPRECATED(10_3,10_11,2_0,9_0,"UseNSURLSession(seeNSURLSession.h)")__WATCHOS_PROHIBITED;
+(nullableNSURLConnection*)connectionWithRequest:(NSURLRequest*)requestdelegate:(nullableid)delegateNS_DEPRECATED(10_3,10_11,2_0,9_0,"UseNSURLSession(seeNSURLSession.h)")__WATCHOS_PROHIBITED;
//异步请求
+(void)sendAsynchronousRequest:(NSURLRequest*)request
queue:(NSOperationQueue*)queue
completionHandler:(void(^)(NSURLResponse*__nullableresponse,NSData*__nullabledata,NSError*__nullableconnectionError))handlerNS_DEPRECATED(10_7,10_11,5_0,9_0,"Use[NSURLSessiondataTaskWithRequest:completionHandler:](seeNSURLSession.h")__WATCHOS_PROHIBITED;
//同步请求
+(nullableNSData*)sendSynchronousRequest:(NSURLRequest*)requestreturningResponse:(NSURLResponse*__nullable*__nullable)responseerror:(NSError**)errorNS_DEPRECATED(10_3,10_11,2_0,9_0,"Use[NSURLSessiondataTaskWithRequest:completionHandler:](seeNSURLSession.h")__WATCHOS_PROHIBITED;

普通任务和上传

NSURLSession针对下载/上传等复杂的网络操作提供了专门的解决方案,针对普通、上传和下载分别对应三种不同的网络请求任务:NSURLSessionDataTask,NSURLSessionUploadTask和NSURLSessionDownloadTask。创建的task都是挂起状态,需要resume才能启动。

当服务器返回的数据较小时,NSURLSession与NSURLConnection执行普通任务的操作步骤没有区别。
执行上传任务时,NSURLSession与NSURLConnection一样需要设置POST请求的请求体进行上传。

下载任务方式

NSURLConnection下载文件时,先是将整个文件下载到内存,然后再写入到沙盒,如果文件比较大,就会出现内存暴涨的情况。

而使用NSURLSessionUploadTask下载文件,会默认下载到沙盒中的tem文件中,不会出现内存暴涨的情况,但是在下载完成后会把tem中的临时文件删除,需要在初始化任务方法时,在completionHandler回调中增加保存文件的代码。(后面会详细说)

请求方法的控制

NSURLConnection实例化对象,实例化开始,默认请求就发送(同步发送),不需要调用start方法。而cancel可以停止请求的发送,停止后不能继续访问,需要创建新的请求。

NSURLSession有三个控制方法,取消(cancel)、暂停(suspend)、继续(resume),暂停以后可以通过继续恢复当前的请求任务。

断点续传的方式

NSURLConnection进行断点下载,通过设置访问请求的HTTPHeaderFtimhbw博客版权所有ield的Range属性,开启运行循环,NSURLConnection的代理方法作为运行循环的事件源,接收到下载数据时代理方法就会持续调用,并使用NSOutputStream管道流进行数据保存。

NSURLSession进行断点下载,当暂停下载任务后,如果downloadTask(下载任务)为非空,调用cancelByProducingResumeData:(void (^)(NSData *resumeData))completionHandler这个方法,这个方法接收一个参数,完成处理代码块,这个代码块有一个NSData参数resumeData,如果resumeData非空,我们就保存这个对象到视图控制器的resumeData属性中,在点击再次下载时,通过调用[ [self.session downloadTaskWithResumeData:self.resumeData]resume]方法进行继续下载操作

经过以上比较可以发现,使用NSURLSession进行断点下载更加便捷.

配置信息

NSURLSession的构造方法(sessionWithConfiguration:delegate:delegateQueue)中有一个NSURLSessionConfiguration类的参数可以设置配置信息,其决定了cookie,安全和高速缓存策略,最大主机连接数,资源管理,网络超时等配置。NSURLConnection不能进行这个配置,相比较与NSURLConnection依赖与一个全局的配置对象,缺乏灵活性而言,NSURLSession有很大的改进了。(关于配置信息,后面会讲解到)

通过以上几点,大概知道了NSURLSession和NSURLConnection的区别,想必下载再遇到这样的问题不会支支吾吾了。下面来点NSURLSession的干货。

干货开始

NSURLSession

NSURLSession 为 HTTP 数据传输提供一系列的接口,而使用 NSURLSession 总共只需要三步:

  1. 创建NSURLSession对象
  2. 通过 NSURLSession 的实例创建 Task
  3. 执行 Task
如何获取Session对象

1.获取默认的 Session 对象

  1. /*
  2. *用于基本的网络请求,可以几行代码就获取URL的内容,使用简单
  3. *无法不断的获取服务器返回的数据
  4. *无法修改默认的连接行为
  5. *身份验证的能力有限
  6. *任务在后台时无法上传和下载
  7. */
  8. +(NSURLSession*)sharedSession;

2.自定义 Session 对象

//不用代理
+(NSURLSession*)sessionWithConfiguration:(NSURLSessionConfiguration*)configuration;
//用代理
+(NSURLSession*)sessionWithConfiguration:(NSURLSessionConfiguration*)configuration
delegate:(nullableid<NSURLSessionDelegate>)delegate
delegateQueue:(nullableNSOperationQueue*)queue;

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

NSURLSessionConfiguration

有三个方法来创建NSURLSessionConfiguration:

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

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

看NSURLSessionConfiguration的头文件:

@interfaceNSURLSessionConfiguration:NSObject<NSCopying>


/*三种创建方式*/


+(NSURLSessionConfiguration*)defaultSessionConfiguration;
+(NSURLSessionConfiguration*)ephemeralSessionConfiguration;
+(NSURLSessionConfiguration*)backgroundSessionConfigurationWithIdentifier:(NSString*)identifierNS_AVAILABLE(10_10,8_0);


/*当使用上述第三种方式创建后台sessionConfiguration时可以读到初始化时传入的唯一标识,其他创建方式都为空*/
@property(nullable,readonly,copy)NSString*identifier;


/*
缓存策略,默认值是NSURLRequestUseProtocolCachePolicy
*/
@propertyNSURLRequestCachePolicyrequestCachePolicy;


/*给request指定每次接收数据超时间隔,如果下一次接受新数据用时超过该值,则发送一个请求超时给该request。默认为60s*/
@propertyNSTimeIntervaltimeoutIntervalForRequest;


/*给指定resource设定一个超时时间,resource需要在时间到达之前完成。默认是7天。*/
@propertyNSTimeIntervaltimeoutIntervalForResource;


/*指定网络传输类型。精切指出传输类型,可以让系统快速响应,提高传输质量,延长电池寿命等。
typedefNS_ENUM(NSUInteger,NSURLRequestNetworkServiceType)
{
NSURLNetworkServiceTypeDefault=0,//普通网络传输,默认使用这个
NSURLNetworkServiceTypeVoIP=1,//网络语音通信传输,只能在VoIP使用
NSURLNetworkServiceTypeVideo=2,//影像传输
NSURLNetworkServiceTypeBackground=3,//网络后台传输,优先级不高时可使用。对用户不需要的网络操作可使用
NSURLNetworkServiceTypeVoice=4//语音传输
};
*/
@propertyNSURLRequestNetworkServiceTypenetworkServiceType;


/*是否使用蜂窝网络,默认是yes.*/
@propertyBOOLallowsCellularAccess;


/*是否由系统根据性能自动裁量后台任务。默认值是NO。同sessionSendsLaunchEvent一样,只对后台configuration有效。*/
@property(getter=isDiscretionary)BOOLdiscretionaryNS_AVAILABLE(10_10,7_0);


/*
如果要为app的插件提供session,需要给这个值赋值
*/
@property(nullable,copy)NSString*sharedContainerIdentifierNS_AVAILABLE(10_10,8_0);


/*
表示当后台传输结束时,是否启动app.这个属性只对后台sessionConfiguration生效,其他configuration类型会自动忽略该值。默认值是YES。
*/
@propertyBOOLsessionSendsLaunchEventsNS_AVAILABLE(NA,7_0);


/*
指定了会话连接中的代理服务器。同样地,大多数面向消费者的应用程序都不需要代理,所以基本上不需要配置这个属性,默认为NULL
*/
@property(nullable,copy)NSDictionary*connectionProxyDictionary;


/*确定是否支持SSLProtocol版本的会话
*/
@propertySSLProtocolTLSMinimumSupportedProtocol;


/*
确定是否支持SSLProtocol版本的会话
*/
@propertySSLProtocolTLSMaximumSupportedProtocol;


/*
它可以被用于开启HTTP管道,这可以显着降低请求的加载时间,但是由于没有被服务器广泛支持,默认是禁用的
*/
@propertyBOOLHTTPShouldUsePipelining;


/*
默认为yes,是否提供来自shareCookieStorge的cookie,如果想要自己提供cookie,可以使用HTTPAdditionalHeaders来提供。
*/
@propertyBOOLHTTPShouldSetCookies;


/*Policyforacceptingcookies.Thisoverridesthepolicyotherwisespecifiedbythecookiestorage.*/
@propertyNSHTTPCookieAcceptPolicyHTTPCookieAcceptPolicy;


/*
指定了一组默认的可以设置出站请求的数据头。这对于跨会话共享信息,如内容类型,语言,用户代理,身份认证,是很有用的。
例如:
@{@"Accept":@"application/json",
@"Accept-Language":@"en",
@"Authorization":authString,
@"User-Agent":userAgentString
}
*/
@property(nullable,copy)NSDictionary*HTTPAdditionalHeaders;


/*
同时连接一个host的最大数。iOS默认是4.APP是作为一个整体来看的
*/
@propertyNSIntegerHTTPMaximumConnectionsPerHost;


/*
存储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;


/*Enableextendedbackgroundidlemodeforanytcpsocketscreated.Enablingthismodeasksthesystemtokeepthesocketopen
*anddelayreclaimingitwhentheprocessmovestothebackground(seehttps://developer.apple.com/library/ios/technotes/tn2277/_index.html)
*/
@propertyBOOLshouldUseExtendedBackgroundIdleModeNS_AVAILABLE(10_11,9_0);


/*
处理NSURLRequest的NSURLProtocol的子类。
重要:对后台Session失效。
*/
@property(nullable,copy)NSArray<Class>*protocolClasses;


@end

现在,我们知道如何来创建一个Session对象了,创建完Session对象,根据一个Request对象我们就可以发送网络请求了。下面看一下NSURLSession的头文件中的这些方法,如图:

从这些方法中得知,分别返回了NSURLSessionDataTaskNSURLSessionUploadTaskNSURLSessionDownloadTaskNSURLSessionStreamTask这四个类的对象,那么这四个类是干什么呢?我们接着往下看。

URLSessionTask

NSURLSessionTask是一个抽象类,其下有4个实体子类可以直接使用:NSURLSessionDataTaskNSURLSessionUploadTaskNSURLSessionDownloadTaskNSURLSessionStreamTask。这四个子类封装了现代程序四个最基本的网络任务:获取数据,比如JSON或者XML,上传文件和下载文件还有数据流的获取。

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

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

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

- (void)cancel;取消任务。

NSURLSessionTask还有个属性,@property (readonly) NSURLSessionTaskState state;此属性标识当前任务的状态,枚举类型

typedefNS_ENUM(NSInteger,NSURLSessionTaskState){
NSURLSessionTaskStateRunning=0,/*正在执行*/
NSURLSessionTaskStateSuspended=1,/*暂停状态*/
NSURLSessionTaskStateCanceling=2,/*取消状态*/
NSURLSessionTaskStateCompleted=3,/*任务完成状态*/
}

上面说到的四个类,都直接或间接继承NSURLSessionTask,所有NSURLSessionTask的方法或者属性这四个类都有,那么,简单说一下这四个类都是干什么的。

NSURLSessionDataTask

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

简单 Get 请求
/**
*简单GET请求
*/
-(void)getWithsharedSession
{
//获取默认Session
NSURLSession*session=[NSURLSessionsharedSession];
//创建URL
NSURL*url=[NSURLURLWithString:@"https://www.baidu.com/s?wd=test"];
//创建任务task
NSURLSessionDataTask*task=[sessiondataTaskWithURL:urlcompletionHandler:^(NSData*_Nullabledata,NSURLResponse*_Nullableresponse,NSError*_Nullableerror){
//获取数据后解析并输出
NSString*dataStr=[[NSStringalloc]initWithData:dataencoding:NSUTF8StringEncoding];
NSLog(@"%@",dataStr);
}];
//启动任务
[taskresume];
}
简单 POST 请求
/**
*简单Post请求,POST和GET请求在于对request的处理不同,其余和GET相同
*/
-(void)postWithSharedSession
{
//获取默认Session
NSURLSession*session=[NSURLSessionsharedSession];
//创建URL
NSURL*url=[NSURLURLWithString:@"http://120.25.226.186:32812/login"];
//创建request
NSMutableURLRequest*request=[NSMutableURLRequestrequestWithURL:url];
//请求方法
request.HTTPMethod=@"POST";
//请求体
request.HTTPBody=[@"username=1234&pwd=4321"dataUsingEncoding:NSUTF8StringEncoding];
//创建任务task
NSURLSessionDataTask*task=[sessiondataTaskWithRequest:requestcompletionHandler:^(NSData*_Nullabledata,NSURLResponse*_Nullableresponse,NSError*_Nullableerror){
//获取数据后解析并输出
NSLog(@"%@",[NSJSONSerializationJSONObjectWithData:dataoptions:kNilOptionserror:nil]);
}];
//启动任务
[taskresume];
}

另外我们也可以设置session的代理来实时的监听数据,我们可以使用NSURLSession的+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;这两个方法来设置代理,具体的协议为NSURLSessionDelegate,它有四个直接或间接子协议NSURLSessionTaskDelegateNSURLSessionDownloadDelegateNSURLSessionStreamDelegateNSURLSessionDataDelegate。具体代理方法如下:

//创建有代理的session
-(void)sessionDataDelegate
{
//创建带有代理方法的自定义session
NSURLSession*session=[NSURLSessionsessionWithConfiguration:[NSURLSessionConfigurationdefaultSessionConfiguration]delegate:selfdelegateQueue:[NSOperationQueuemainQueue]];


//创建任务
NSURLSessionDataTask*task=[sessiondataTaskWithRequest:[NSURLRequestrequestWithURL:[NSURLURLWithString:@"http://120.25.226.186:32812/login?username=1234&pwd=4321"]]];


//启动任务
[taskresume];
}


#pragmamark-
#pragmamark-NSURLSessionDelegate


//1.接收到服务器的响应
-(void)URLSession:(NSURLSession*)sessiondataTask:(NSURLSessionDataTask*)dataTaskdidReceiveResponse:(NSURLResponse*)responsecompletionHandler:(void(^)(NSURLSessionResponseDisposition))completionHandler
{
NSLog(@"接收到服务器的响应");
//必须设置对响应进行允许处理才会执行后面两个操作。
completionHandler(NSURLSessionResponseAllow);
}


//2.接收到服务器的数据(可能调用多次)
-(void)URLSession:(NSURLSession*)sessiondataTask:(NSURLSessionDataTask*)dataTaskdidReceiveData:(NSData*)data{
//处理每次接收的数据
NSLog(@"接受到服务器的数据%s",__func__);
}


//3.请求成功或者失败(如果失败,error有值)
-(void)URLSession:(NSURLSession*)sessiontask:(NSURLSessionTask*)taskdidCompleteWithError:(NSError*)error{
//请求完成,成功或者失败的处理
NSLog(@"SessionTask%s",__func__);
}

NSURLSessionDownloadTask

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

/**
*NSURLSessionDownloadTask下载任务
*/
-(void)downLoad
{
NSURLSession*session=[NSURLSessionsharedSession];
NSURL*url=[NSURLURLWithString:@"http://120.25.226.186:32812/resources/images/minion_01.png"];
NSURLSessionDownloadTask*task=[sessiondownloadTaskWithURL:urlcompletionHandler:^(NSURL*location,NSURLResponse*response,NSError*error){
//location是沙盒中tmp文件夹下的一个临时url,文件下载后会存到这个位置,由于tmp中的文件随时可能被删除,所以我们需要自己需要把下载的文件挪到Caches文件夹中
NSString*path=[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES)lastObject]stringByAppendingPathComponent:response.suggestedFilename];
//剪切文件
[[NSFileManagerdefaultManager]moveItemAtURL:locationtoURL:[NSURLfileURLWithPath:path]error:nil];
NSLog(@"%@",[NSThreadcurrentThread]);
//切记当前为子线程,
dispatch_async(dispatch_get_main_queue(),^{
self.imageView.image=[UIImageimageNamed:path];
});


}];
//启动任务
[taskresume];
}

代理方法下载

/**
*NSURLSessionDownloadTask代理
*/
-(void)sessionDownloadTaskDelegate
{
//创建带有代理方法的自定义session
NSURLSession*session=[NSURLSessionsessionWithConfiguration:[NSURLSessionConfigurationdefaultSessionConfiguration]delegate:selfdelegateQueue:[NSOperationQueuemainQueue]];


//创建任务
NSURLSessionDownloadTask*task=[sessiondownloadTaskWithURL:[NSURLURLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"]];


//启动任务
[taskresume];
}
#pragmamark-
#pragmamark-NSURLSessionDownloadDelegate
/**
*写入临时文件时调用
*@parambytesWritten本次写入大小
*@paramtotalBytesWritten已写入文件大小
*@paramtotalBytesExpectedToWrite请求的总文件的大小
*/
-(void)URLSession:(NSURLSession*)sessiondownloadTask:(nonnullNSURLSessionDownloadTask*)downloadTaskdidWriteData:(int64_t)bytesWrittentotalBytesWritten:(int64_t)totalBytesWrittentotalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
//可以监听下载的进度
CGFloatprogress=1.0*totalBytesWritten/totalBytesExpectedToWrite;
NSLog(@"downloadTask%f",progress);
}


//2.下载完成调用
-(void)URLSession:(NSURLSession*)sessiondownloadTask:(NSURLSessionDownloadTask*)downloadTaskdidFinishDownloadingToURL:(NSURL*)location
{
//location还是一个临时路径,需要自己挪到需要的路径(caches文件夹)
NSString*filePath=[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES)lastObject]stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
[[NSFileManagerdefaultManager]moveItemAtURL:locationtoURL:[NSURLfileURLWithPath:filePath]error:nil];
NSLog(@"downloadTask移动文件路径");
}
断点续传

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

  • 某种限制,续传暂停
  • 将暂停后数据(当前数据)保存起来--_resumeData = resumeData;
  • 条件允许续传时,使用resumeData创建新的NSURLSessionTask

代码:

-(IBAction)startDowning:(id)sender{




if(_resumeData){
_downloadTask=[_sessiondownloadTaskWithResumeData:_resumeData];
}else{
_session=[NSURLSessionsessionWithConfiguration:[NSURLSessionConfigurationdefaultSessionConfiguration]delegate:selfdelegateQueue:nil];
_request=[[NSURLRequestalloc]initWithURL:[NSURLURLWithString:@"http://image.baidu.com/search/down?tn=download&ipn=dwnl&word=download&ie=utf8&fr=result&url=http%3A%2F%2Fb.zol-img.com.cn%2Fdesk%2Fbizhi%2Fimage%2F6%2F960x600%2F1427787678554.jpg&thumburl=http%3A%2F%2Fimg3.imgtn.bdimg.com%2Fit%2Fu%3D1996019669%2C1779575266%26fm%3D21%26gp%3D0.jpg"]];
_downloadTask=[_sessiondownloadTaskWithRequest:_request];
}
[_downloadTaskresume];
}


-(IBAction)stopDowning:(id)sender{
if(_downloadTask){
__weaktypeof(self)weakSelf=self;
[_downloadTaskcancelByProducingResumeData:^(NSData*_NullableresumeData){
NSLog(@"%@",resumeData);
weakSelf.resumeData=resumeData;
weakSelf.downloadTask=nil;
}];
}
}


#pragmamark-
#pragmamark-NSURLSessionDownloadDelegate


-(void)URLSession:(NSURLSession*)sessiondownloadTask:(NSURLSessionDownloadTask*)downloadTask
didFinishDownloadingToURL:(NSURL*)location{
NSString*path=[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES)lastObject]stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
//剪切文件
[[NSFileManagerdefaultManager]moveItemAtURL:locationtoURL:[NSURLfileURLWithPath:path]error:nil];


dispatch_async(dispatch_get_main_queue(),^{
self.imageView.image=[UIImageimageNamed:path];
});
}
-(void)URLSession:(NSURLSession*)sessiondownloadTask:(NSURLSessionDownloadTask*)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
CGFloatprogress=1.0*totalBytesWritten/totalBytesExpectedToWrite;
NSLog(@"downloadTask%f",progress);
dispatch_async(dispatch_get_main_queue(),^{
self.progressView.progress=progress;
});


}

注意:上面的代码是不会断点续传的,原因是这个图片的url不支持断点续传,在断点续传时,要和服务器配合好。

NSURLSessionUploadTask

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

  1. -(NSURLSessionUploadTask*)uploadTaskWithRequest:(NSURLRequest*)requestfromFile:(NSURL*)fileURL;
  2. -(NSURLSessionUploadTask*)uploadTaskWithRequest:(NSURLRequest*)requestfromData:(NSData*)bodyData;

我们这里使用第二个方法,表单的形式上传数据

-(void)upload{
NSMutableURLRequest*request=[NSMutableURLRequestrequestWithURL:[NSURLURLWithString:@"https://api.weibo.com/2/statuses/public_timeline.json"]];
//设置HTTP请求方式GET/POST
[requestsetHTTPMethod:@"POST"];
//设置请求头
NSString*boundary=@"hwg";
[requestsetValue:[NSStringstringWithFormat:@"multipart/form-data;%@",boundary]forHTTPHeaderField:@"Content-type"];
//设置请求体
//获取上传的图片的data
NSData*data=[NSDatadataWithContentsOfFile:[[NSBundlemainBundle]pathForResource:@"xiaoxin"ofType:@"jpeg"]];
//此处添加需要看清楚内容
NSData*body=[selfhttpFormDataBodyWithBoundary:boundaryparams:@{@"access_token":@"2.00cYYKWF6EKpiB3883361b1dJiZ4eD",@"status":@"哈哈,这是我测试NSURLSession上传文件的微博"}fieldName:@"pic"fileName:@"pic.png"fileContentType:@"image/png"data:data];
NSURLSession*session=[NSURLSessionsharedSession];
NSURLSessionUploadTask*upload_task=[sessionuploadTaskWithRequest:requestfromData:bodycompletionHandler:^(NSData*data,NSURLResponse*response,NSError*error){
NSLog(@"uploadsuccess");
}];
//必须要resume
[upload_taskresume];
}


#pragmamark-拼接请求体
-(NSData*)httpFormDataBodyWithBoundary:(NSString*)boundary
params:(NSDictionary*)params
fieldName:(NSString*)fieldName
fileName:(NSString*)fileName
fileContentType:(NSString*)fileContentType
data:(NSData*)fileData{


NSString*preBoundary=[NSStringstringWithFormat:@"--%@",boundary];
NSString*endBoundary=[NSStringstringWithFormat:@"--%@--",boundary];
NSMutableString*body=[[NSMutableStringalloc]init];
//遍历
for(NSString*keyinparams){
//得到当前的key
//如果key不是当前的pic,说明value是字符类型,比如name:Boris
//添加分界线,换行,必须使用\r\n
[bodyappendFormat:@"%@\r\n",preBoundary];
//添加字段名称换2行
[bodyappendFormat:@"Content-Disposition:form-data;name=\"%@\"\r\n\r\n",key];
//添加字段值
[bodyappendFormat:@"%@\r\n",[paramsobjectForKey:key]];


}
//添加分界线,换行
[bodyappendFormat:@"%@\r\n",preBoundary];
//声明pic字段,文件名为boris.png
[bodyappendFormat:@"Content-Disposition:form-data;name=\"%@\";filename=\"%@\"\r\n",fieldName,fileName];
//声明上传文件的格式
[bodyappendFormat:@"Content-Type:%@\r\n\r\n",fileContentType];
//声明结束符
NSString*endStr=[NSStringstringWithFormat:@"\r\n%@",endBoundary];
//声明myRequestData,用来放入httpbody
NSMutableData*myRequestData=[NSMutableDatadata];
//将body字符串转化为UTF8格式的二进制
[myRequestDataappendData:[bodydataUsingEncoding:NSUTF8StringEncoding]];
//将image的data加入
[myRequestDataappendData:fileData];
//加入结束符--hwg--
[myRequestDataappendData:[endStrdataUsingEncoding:NSUTF8StringEncoding]];
returnmyRequestData;
}

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

-(void)URLSession:(NSURLSession*)sessiontask:(NSURLSessionTask*)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
CGFloatprogress=1.0*totalBytesSent/bytesSent;
NSLog(@"downloadTask%f",progress);
}

结语

以上就是我今天总结的,哪里有问题还希望大家提出意见。其实在开发中,我们很少使用到这些,因为总是有一些牛人为我们封装了各种功能的强大库,比如网络类,最常用的就是AFNetworking。人家的库为什么好用,说白了就是各种情况都考虑到了。所有我们要学的还是人家的编程思想。接下来,我会做一个专题,研究一下各大平常使用到的库,它们到底牛在哪里,敬请阅读!我的博客www.guiyongdong.com

转载自:www.guiyongdong.com

0 条回应