zoukankan      html  css  js  c++  java
  • iOS

    前言

    	@interface NSURLConnection : NSObject
    	class NSURLConnection : NSObject
    	
    	DEPRECATED: The NSURLConnection class should no longer be used.  NSURLSession is the replacement for NSURLConnection
    
    	从 iOS 9 开始 NSURLConnection 的大部分方法被废弃。
    

    1、NSURLConnection

    • NSURLConnection 提供了两种方式来实现连接,一种是同步的另一种是异步的,异步的连接将会创建一个新的线程,这个线程将会来负责下载的动作。而对于同步连接,在下载连接和处理通讯时,则会阻塞当前调用线程。

    • 许多开发者都会认为同步的连接将会堵塞主线程,其实这种观点是错误的。一个同步的连接是会阻塞调用它的线程。如果你在主线程中创建一个同步连接,没错,主线程会阻塞。但是如果你并不是从主线程开启的一个同步的连接,它将会类似异步的连接一样。因此这种情况并不会堵塞你的主线程。事实上,同步和异步的主要区别就是运行 runtime 为会异步连接创建一个线程,而同步连接则不会。

    1.1 NSURLConnection 的常用类

    • 1、NSURL:请求地址;

    • 2、NSURLRequest:封装一个请求,保存发给服务器的全部数据,包括一个 NSURL 对象,请求方法、请求头、请求体 ....;

    • 3、NSMutableURLRequest:NSURLRequest 的子类

    • 4、NSURLConnection:负责发送请求,建立客户端和服务器的连接。发送 NSURLRequest 的数据给服务器,并收集来自服务器的响应数据。

    1.2 使用 NSURLConnection 发送请求的步骤

    • 1、创建一个 NSURL 对象,设置请求路径(设置请求路径);

    • 2、传入 NSURL 创建一个 NSURLRequest 对象,设置请求头和请求体(创建请求对象);

      • 任何 NSURLRequest 默认都是 GET 请求。
    • 3、使用 NSURLConnection 发送 NSURLRequest(发送请求)。

      • 发送同步请求:有返回值。
      • 发送异步请求:没有返回值。

    1.3 发送同步请求

    • 使用 NSURLConnection 的 sendSynchronousRequest:returningResponse:error: 类方法,我们可以进行同步请求。

    • 在创建一个同步的网络连接的时候我们需要明白一点,并不是是我们的这个同步连接一定会堵塞我们的主线程,如果这个同步的连接是创建在主线程上的,那么这种情况下是会堵塞我们的主线程的,其他的情况下是不一定会堵塞我们的主线程的。例如如果在 GCD 的全局并发队列上初始化了一个同步的连接,其实并不会堵塞我们的主线程的。

    1.4 发送异步请求

    • 发送异步请求有两种方式:

      • 1)使用 block 回调:

        	NS_AVAILABLE(10_7, 5_0)
        	+ (void)sendAsynchronousRequest:(NSURLRequest*) request
                                    queue:(NSOperationQueue*) queue
                        completionHandler:(void (^)(NSURLResponse* response, NSData* data, NSError* connectionError)) handler;
        
        		创建一个操作,放在 NSOperation 队列中执行,默认是异步执行的。当服务器有返回数据的时候调用会开一条新的线程去发送请求,主线程继续往下走,
        	当拿到服务器的返回数据的数据的时候再回调 block,执行 block 代码段。这种情况不会卡住主线程。
        
        		queue 队列的作用是决定这个 block 操作放在哪个线程执行?刷新 UI 界面的操作应该放在主线程执行,不能放在子线程,在子线程处理 UI 相关操作
        	会出现一些莫名的问题。使用 [NSOperationQueue mainQueue] 返回一个和主线程相关的队列,即主队列,这个 block 操作会被放在主线程中执行。使用 
        	[[NSOperationQueue alloc] init] 返回的不是主队列,这个 block 操作不会被放在主线程中执行。
        
      • 2)代理:

        	- (instancetype)initWithRequest:(NSURLRequest *)request delegate:(id)delegate;
        	+ (NSURLConnection*)connectionWithRequest:(NSURLRequest *)request delegate:(id)delegate;
        
        	要监听服务器返回的 data,所以使用 <NSURLConnectionDataDelegate> 协议。
        
        	当接收到服务器的响应(连通了服务器)时会调用:
        		- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
        
        	当接收到服务器的数据时会调用(可能会被调用多次,每次只传递部分数据,需要拼接接收到的所有数):
        		- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
        
        	当服务器的数据加载完毕时就会调用:
        		- (void)connectionDidFinishLoading:(NSURLConnection *)connection
        
        	请求错误(失败)的时候调用(请求超时断网没有网,一般指客户端错误):
        		- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
        
        	NSURLConnection 的代理方法默认都是在主线程上执行的,会对界面产生卡顿。
        
        	For the connection to work correctly, the calling thread’s run loop must be operating in the default run loop mode.
        	为了让连接工作正常,调用线程的运行循环必须在默认的运行循环模式下。
        
        		如果要让 NSURLConnection 实现在后台线程回调代理方法,需要在后台线程启动 NSURLConnection,并启动后台线程的运行循环,NSURLConnection 
        	执行完毕后,会自动停止后台线程的运行循环。
        
        		启动子线程的运行循环方法:
        			CFRunLoopRun();
        			[[NSRunLoop currentRunLoop] run];        // NSRunLoop 只能启动,没有提供停止的接口
        

    1.5 开始/停止网络连接

        - (void)start;
        - (void)cancel;
    
    • 创建网络连接后可以不使用 start,系统会自动开始网络连接。

    • 取消一个请求后,连接不在调用代理方法,如果希望再此连接,需要再次创建一个新的网络连接。

    1.6 NSURLConnectionDownloadDelegate

    • NSURLConnectionDownloadDelegate 代理方法是为 Newsstand Kit’s(杂志包) 创建的下载服务的,Newsstand 主要在国外使用比较广泛,国内极少。
    • 如果使用 NSURLConnectionDownloadDelegate 代理方法监听下载进度,能够监听到进度,但是找不到下载的文件。

    2、NSURLConnection 同步 GET 请求

    • Objective-C

      	// 设置网络接口
      	NSString *urlStr = @"http://192.168.88.200:8080/MJServer/video?type=JSON";
      
      	// 设置请求路径
      	NSURL *url = [NSURL URLWithString:urlStr];
      
      	NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
        
      	// 创建同步网络请求
      	NSData *syncNetData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:NULL error:NULL];          	
      
    • Swift

      	// 设置网络接口
      	let urlStr = "http://192.168.88.200:8080/MJServer/video?type=JSON"
      
      	// 设置请求路径
      	let url = NSURL(string: urlStr!)
      
      	let urlRequest = NSURLRequest(URL: url!)
      
      	// 创建同步网络请求
      	let syncNetData = try? NSURLConnection.sendSynchronousRequest(urlRequest, returningResponse: nil)
      

    3、NSURLConnection 同步 POST 请求

    • Objective-C

      	// 设置网络接口
      	NSURL *url = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video"];
      
      	// 创建请求对象
      	NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
      
      	// 设置请求方式,默认为 GET 请求
      	urlRequest.HTTPMethod = @"POST";
      
      	// 设置请求体(请求参数)
      	urlRequest.HTTPBody = [@"type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
      
      	// 创建同步网络请求
      	NSData *syncNetData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:NULL error:NULL];
      
    • Swift

      	// 设置网络接口
      	let url = NSURL(string: "http://192.168.88.200:8080/MJServer/video")
      
      	// 创建请求对象
      	let urlRequest = NSMutableURLRequest(URL: url!)
      
      	// 设置请求方式,默认为 GET 请求
      	urlRequest.HTTPMethod = "POST";
      
      	// 设置请求体(请求参数)
      	urlRequest.HTTPBody = "type=JSON".dataUsingEncoding(NSUTF8StringEncoding)
      
      	// 创建同步网络请求
      	let syncNetData = try? NSURLConnection.sendSynchronousRequest(urlRequest, returningResponse: nil)
      

    4、NSURLConnection 异步 GET 请求

    • Objective-C

      • 使用 block 回调方式

        	// 设置网络接口
        	NSURL *url = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video?type=XML"];
        
        	// 创建请求对象
        	NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
        
        	// 创建异步网络请求
        	[NSURLConnection sendAsynchronousRequest:urlRequest 
        	                                   queue:[NSOperationQueue mainQueue] 
        	                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        
            	if (connectionError == nil && data != nil) {
        
            	}
        	}];
        
      • 使用 协议 方式

        	// 遵守协议 <NSURLConnectionDataDelegate>
        
        	@property(nonatomic, retain)NSMutableData *asyncNetData;
        
        	// 设置网络接口
        	NSURL *url = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video?type=XML"];
        
        	// 创建请求对象
        	NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
        
        	// 创建异步网络请求
        	[NSURLConnection connectionWithRequest:urlRequest delegate:self];
        
        	// 协议方法
        
        	// 接收到服务器的响应
        	- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
        
        		// 异步下载数据源初始化
            	self.asyncNetData = [[NSMutableData alloc] init];
        	}
        
        	// 接收到服务器数据
        	- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
        
        		// 拼接从服务器下载的数据
            	[self.asyncNetData appendData:data];
        	}
        
        	// 服务器的数据加载完毕
        	- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
        
        		// 处理从服务器下载的数据
            	self.textView.text = [[NSString alloc] initWithData:self.asyncNetData encoding:NSUTF8StringEncoding];
        	}
        
        	// 请求错误
        	- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
        
        	}
        
    • Swift

      • 使用 闭包 方式

        	// 设置网络接口
        	let url = NSURL(string: "http://192.168.88.200:8080/MJServer/video?type=XML")
        
        	// 创建请求对象
        	let urlRequest = NSURLRequest(URL: url!)
        
        	// 创建异步网络请求
        	NSURLConnection.sendAsynchronousRequest(urlRequest, 
        	                                  queue: NSOperationQueue.mainQueue()) 
        	                                       { (response:NSURLResponse?, data:NSData?, connectionError:NSError?) -> Void in
        
            	if connectionError == nil {
        
        			// 服务器的数据加载完毕,处理从服务器下载的数据
            	}
        	}
        
      • 使用 协议 方式

        	// 遵守协议 NSURLConnectionDataDelegate
        
        	// 设置网络接口
        	let url = NSURL(string: "http://192.168.88.200:8080/MJServer/video?type=XML")
        
        	// 创建请求对象
        	let urlRequest = NSURLRequest(URL: url!)
        
        	// 创建异步网络请求
        	NSURLConnection(request: urlRequest, delegate: self)
        
        	// 协议方法
        	
        	// 接收到服务器的响应
        	func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse) {
        
        		// 异步下载数据源初始化
            	self.asyncNetData = NSMutableData()
        	}
        
        	// 接收到服务器数据
        	func connection(connection: NSURLConnection, didReceiveData data: NSData) {
        
        		// 拼接从服务器下载的数据
            	self.asyncNetData?.appendData(data)
        	}
        
        	// 服务器的数据加载完毕
        	func connectionDidFinishLoading(connection: NSURLConnection) {
        
        		// 处理从服务器下载的数据
            	self.textView.text = NSString(data: self.asyncNetData!, encoding: NSUTF8StringEncoding) as! String
        	}
        
        	// 请求错误
        	func connection(connection: NSURLConnection, didFailWithError error: NSError) {
        
        	}
        

    5、NSURLConnection 异步 POST 请求

    • Objective-C

      • 使用 block 回调方式

        	// 设置网络接口
        	NSURL *url = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video"];
        
        	// 创建请求对象
        	NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
        
        	// 设置请求方式,默认为 GET 请求
        	urlRequest.HTTPMethod = @"POST";
        
        	// 设置请求体(请求参数)
        	urlRequest.HTTPBody = [@"type=XML" dataUsingEncoding:NSUTF8StringEncoding];
        
        	// 创建异步网络请求
        	[NSURLConnection sendAsynchronousRequest:urlRequest 
        	                                   queue:[NSOperationQueue mainQueue] 
        	                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        
            	if (connectionError == nil && data != nil) {
        
            	}
        	}];
        
      • 使用 协议 方式

        	// 遵守协议 <NSURLConnectionDataDelegate>
        
        	// 设置网络接口
        	NSURL *url = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video"];
        
        	// 创建请求对象
        	NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
        
        	// 设置请求方式,默认为 GET 请求
        	urlRequest.HTTPMethod = @"POST";
        
        	// 设置请求体(请求参数)
        	urlRequest.HTTPBody = [@"type=XML" dataUsingEncoding:NSUTF8StringEncoding];
        
        	// 创建异步网络请求
        	[NSURLConnection connectionWithRequest:urlRequest delegate:self];
        
        	// 协议方法
        
        	// 已经发送请求体
        	- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten 
        	                                               totalBytesWritten:(NSInteger)totalBytesWritten 
        	                                       totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite {
        
        	}
        
        	// 接收到服务器的响应
        	- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
        
        	}
        
        	// 接收到服务器数据
        	- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
        
        	}
        
        	// 服务器的数据加载完毕
        	- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
        
        	}
        
        	// 请求错误
        	- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
        
        	}
        
    • Swift

      • 使用 闭包 回调方式

        	// 设置网络接口
        	let url = NSURL(string: "http://192.168.88.200:8080/MJServer/video")
        
        	// 创建请求对象
        	let urlRequest = NSMutableURLRequest(URL: url!)
        
        	// 设置请求方式,默认为 GET 请求
        	urlRequest.HTTPMethod = "POST"
        
        	// 设置请求体(请求参数)
        	urlRequest.HTTPBody = "type=XML".dataUsingEncoding(NSUTF8StringEncoding)
        
        	// 创建异步网络请求
        	NSURLConnection.sendAsynchronousRequest(urlRequest, 
        	                                  queue: NSOperationQueue.mainQueue()) 
        	                                       { (response:NSURLResponse?, data:NSData?, connectionError:NSError?) in
        
        	}
        
      • 使用 协议 方式

        	// 遵守协议 <NSURLConnectionDataDelegate>
        
        	// 设置网络接口
        	let url = NSURL(string: "http://192.168.88.200:8080/MJServer/video")
        
        	// 创建请求对象
        	let urlRequest = NSMutableURLRequest(URL: url!)
        
        	// 设置请求方式,默认为 GET 请求
        	urlRequest.HTTPMethod = "POST"
        
        	// 设置请求体(请求参数)
        	urlRequest.HTTPBody = "type=XML".dataUsingEncoding(NSUTF8StringEncoding)
        
        	// 创建异步网络请求
        	NSURLConnection(request: urlRequest, delegate: self)
        
        	// 协议方法
        
        	// 已经发送请求体
        	func connection(connection: NSURLConnection, didSendBodyData bytesWritten: Int, 
        	                                                        totalBytesWritten: Int, 
        	                                                totalBytesExpectedToWrite: Int) {
        
        	}
        
        	// 接收到服务器的响应
        	func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse) {
        
        	}
        
        	// 接收到服务器数据
        	func connection(connection: NSURLConnection, didReceiveData data: NSData) {
        
        	}
        
        	// 服务器的数据加载完毕
        	func connectionDidFinishLoading(connection: NSURLConnection) {
        
        	}
        
        	// 请求错误
        	func connection(connection: NSURLConnection, didFailWithError error: NSError) {
        
        	}
        

    6、NSURLConnection 文件下载

    6.1 获取文件信息

    • Objective-C

      	NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/download/file/minion_02.mp4"];
      
      	NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
      	
      	// 使用 HEAD 请求方式
      	request.HTTPMethod = @"HEAD";
      
      	NSURLResponse *response = nil;
      	NSError *error = nil;
      	
      	// 使用同步请求方式,后续的下载会依赖于此
      	[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
      
      	if (error == nil && response != nil) {
          
          	// 获取文件大小或名称
          	NSLog(@"要下载文件的长度 %tu", response.expectedContentLength);
      	}
      

    6.2 使用 GET 数据请求方式下载,文件句柄存储

    • Objective-C

      	// 下载文件的总长度
      	@property (nonatomic, assign) long long expectedContentLength;
      
      	// 当前文件大小
      	@property (nonatomic, assign) long long recvedfileLength;
      
      	NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/download/file/minion_02.mp4"];
      	NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
      
      	// 遵守协议 <NSURLConnectionDataDelegate>
      	[NSURLConnection connectionWithRequest:urlRequest delegate:self];
      
      	// 协议方法
      
      	- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
          
          	NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] 
          	                                                                   stringByAppendingPathComponent:@"321.mp4"];
          	
          	// 如果文件不存在,方法不会出错
          	[[NSFileManager defaultManager] removeItemAtPath:documentsPath error:NULL];
      
          	// 获取数据总大小
          	self.expectedContentLength = response.expectedContentLength;
      
          	self.recvedfileLength = 0;
      	}
      
      	- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
      
      		// 将从服务器下载的数据直接写入文件
          	[self writeToFile:data];
      
      		// 计算当前数据下载完成的大小
          	self.recvedfileLength += data.length;
      
          	// 计算下载进度	
          	float progress = (float)self.recvedfileLength / self.expectedContentLength;
      	}
      
      	- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
          	
      	}
      
      	- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
          	
      	}
      
      	// 将数据写入文件
      	- (void)writeToFile:(NSData *)data {
          
          	/*
              	NSFileManager:文件管理器,文件复制,删除,是否存在...操作,类似于在 Finder 中进行的操作
              	NSFileHandle :文件 "句柄(指针)" Handle 操纵杆,凡事看到 Handle 单词,表示对前面一个名词(File)的操作,
              	               对一个文件进行独立的操作。
           	*/
          
          	NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] 
          	                                                                   stringByAppendingPathComponent:@"321.mp4"];
          
          	// 打开文件,如果文件不存在,fp == nil
          	NSFileHandle *fp = [NSFileHandle fileHandleForWritingAtPath:documentsPath];
      
          	// 如果文件不存在
          	if (fp == nil) {
      
          		// 将数据直接写入文件
              	[data writeToFile:documentsPath atomically:YES];
          	} else {
          		
          		// 将文件指针移动到文件末尾
              	[fp seekToEndOfFile];
      
              	// 写入数据
              	[fp writeData:data];
      
              	// 关闭文件,C 语言中有一个默认的约定,凡事打开文件,都必须关闭
              	[fp closeFile];
          	}
      	}
      

    6.3 使用 GET 数据请求方式下载,文件输出流存储

    • Objective-C

      	// 下载文件的总长度
      	@property (nonatomic, assign) long long expectedContentLength;
      
      	// 当前文件大小
       	@property (nonatomic, assign) long long recvedfileLength;
      
      	// 输出数据流
      	@property (nonatomic, strong) NSOutputStream *fileStream;
      
      	NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/download/file/minion_02.mp4"];
      	NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
      
      	// 遵守协议 <NSURLConnectionDataDelegate>
      	[NSURLConnection connectionWithRequest:urlRequest delegate:self];
      
      	// 协议方法
      
      	- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
          
          	NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]
          	                                                                   stringByAppendingPathComponent:@"312.mp4"];
          	
          	// 如果文件不存在,方法不会出错
          	[[NSFileManager defaultManager] removeItemAtPath:documentsPath error:NULL];
      
          	// 以拼接的方式实例化文件流
          	self.fileStream = [[NSOutputStream alloc] initToFileAtPath:documentsPath append:YES];
      
          	// 打开文件流
          	[self.fileStream open];
      
          	// 获取数据总大小
          	self.expectedContentLength = response.expectedContentLength;
      
          	self.recvedfileLength = 0;
      	}
      
      	- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
          
          	// 将数据的 "字节"一次性写入文件流,并且指定数据长度
          	[self.fileStream write:data.bytes maxLength:data.length];
      
          	// 计算当前数据下载完成的大小
          	self.recvedfileLength += data.length;
      
          	// 计算下载进度	
          	float progress = (float)self.recvedfileLength / self.expectedContentLength;
      	}
      
      	- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
        	
        		// 关闭文件流
          	[self.fileStream close];
      	}
      
      	- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
        	
        		// 关闭文件流
          	[self.fileStream close];
      	}
      

    6.4 使用 专用下载 方式

    • Objective-C

      	NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/download/file/minion_01.mp4"];
      	NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
      
      	// 遵守协议 <NSURLConnectionDownloadDelegate>
      	[NSURLConnection connectionWithRequest:urlRequest delegate:self];
      
      	// 协议方法
      
      	- (void)connection:(NSURLConnection *)connection didWriteData:(long long)bytesWritten 
      	                                            totalBytesWritten:(long long)totalBytesWritten 
      	                                           expectedTotalBytes:(long long)expectedTotalBytes {
      
          	/*
               	下载进度:
      
               	bytesWritten            本次下载子节数
               	totalBytesWritten       已经下载字节数
               	expectedTotalBytes      总下载字节数(文件总大小)
           	*/
      
          	float progress = (float)totalBytesWritten / expectedTotalBytes;
          	NSLog(@"%f", progress);
      	}
      
      	- (void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *) destinationURL {
      
          	/*
              	destinationURL          下载保存的路径,下载完成之后,找不到下载的文件。
           	*/
      
          	NSLog(@"%@", destinationURL);
      	}
      

    6.5 断点续传下载

    • Objective-C

      	// 下载文件的总长度
      	@property (nonatomic, assign) long long expectedContentLength;
      
      	// 当前文件大小
      	@property (nonatomic, assign) long long recvedfileLength;
      
      	// 输出数据流
      	@property (nonatomic, strong) NSOutputStream *fileStream;
      
      	// 目标目录
      	@property (nonatomic, copy) NSString *targetPath;
      
      	@property (nonatomic, strong) NSURLConnection *conn;
      
      	NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/download/file/minion_02.mp4"];
      
      	// 检查服务器上的文件信息
      	[self checkServerFileInfoWithURL:url];
      
      	// 检查本地文件信息
      	long long fileSize = [self checkLocalFileInfo];
      
      	// 文件已经下载到本地
      	if (fileSize == self.expectedContentLength) {
      
          	return;
      	}
      	
      	// 根据本地文件的长度,从对应 "偏移" 位置开始下载
      	[self downloadWithURL:url offset:fileSize];
      
      	// 检查服务器上的文件信息
      	- (void)checkServerFileInfoWithURL:(NSURL *)url {
          
          	NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
          	request.HTTPMethod = @"HEAD";
          
          	NSURLResponse *response = nil;
          	NSError *error = nil;
          	
          	// 发送同步方法
          	[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
          
          	if (error == nil && response != nil) {
          	
          		// 文件大小
              	self.expectedContentLength = response.expectedContentLength;
              	
              	// 文件名,保存到临时文件夹                                          
              	self.targetPath = [NSTemporaryDirectory() stringByAppendingPathComponent:response.suggestedFilename];
          	}
      	}
      
      	// 检查本地文件信息
      	- (long long)checkLocalFileInfo {
          
          	long long fileSize = 0;
          
          	// 检查本地是否存在文件
          	if ([[NSFileManager defaultManager] fileExistsAtPath:self.targetPath]) {
              
              	NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:self.targetPath error:NULL];
              	
              	// 获取文件大小
              	fileSize = [dict fileSize];
      		}
          
          	// 判断是否比服务器的文件大
          	if (fileSize > self.expectedContentLength) {
          	
          		// 删除文件                                                                 
              	[[NSFileManager defaultManager] removeItemAtPath:self.targetPath error:NULL];
              	fileSize = 0;
          	}
          
          	return fileSize;
      	}
      
      	// 从断点处开始下载
      	- (void)downloadWithURL:(NSURL *)url offset:(long long)offset {
          
          	// 记录本地文件大小
          	self.recvedfileLength = offset;
          
          	NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url 
          	                                                          cachePolicy:1 
          	                                                      timeoutInterval:15];
          	
          	// 一旦设置了 Range,response 的状态码会变成 206
          	[urlRequest setValue:[NSString stringWithFormat:@"bytes=%lld-", offset] forHTTPHeaderField:@"Range"];
          
          	// 遵守协议 <NSURLConnectionDataDelegate>
          	self.conn = [NSURLConnection connectionWithRequest:urlRequest delegate:self];
          
          	[self.conn start];
      	}
      
      	// 暂停下载
      
      	- (void)pause1 {
          
          	[self.conn cancel];
      	}
      
      	// 协议方法
      
      	- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
          
          	// 以拼接的方式实例化文件流
          	self.fileStream = [[NSOutputStream alloc] initToFileAtPath:self.targetPath append:YES];
          	[self.fileStream open];
      	}
      
      	- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
          
          	// 将数据的 "字节"一次性写入文件流,并且指定数据长度
          	[self.fileStream write:data.bytes maxLength:data.length];
          
          	// 计算当前数据下载完成的大小
          	self.recvedfileLength += data.length;
          	
          	// 计算下载进度                                                                 
          	float progress = (float)self.recvedfileLength / self.expectedContentLength;
      	}
      
      	- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
          
          	[self.fileStream close];
      	}
      
      	- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
          
          	// 关闭文件流
          	[self.fileStream close];
      	}
      

    6.6 异步下载

    • Objective-C

      	// 下载文件的总长度
      	@property (nonatomic, assign) long long expectedContentLength;
      
      	// 当前文件大小
      	@property (nonatomic, assign) long long recvedfileLength;
      
      	// 输出数据流
       	@property (nonatomic, strong) NSOutputStream *fileStream;
      
      	dispatch_async(dispatch_get_global_queue(0, 0), ^{
      
          	NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/download/file/minion_02.mp4"];
          	NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
          
          	// 遵守协议 <NSURLConnectionDataDelegate>
          	[NSURLConnection connectionWithRequest:urlRequest delegate:self];
      
      		// 启动运行循环
          	CFRunLoopRun();
      	});
      	
      	// 协议方法
      
      	- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
      
          	NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, 
          	                                                               NSUserDomainMask, 
          	                                                               YES)[0] 
          	                                                  stringByAppendingPathComponent:@"312.mp4"];
          	
          	// 如果文件不存在,方法不会出错
          	[[NSFileManager defaultManager] removeItemAtPath:documentsPath error:NULL];
      
      		// 以拼接的方式实例化文件流
          	self.fileStream = [[NSOutputStream alloc] initToFileAtPath:documentsPath append:YES];
      
          	// 打开文件流
          	[self.fileStream open];
      
      		// 获取数据总大小
          	self.expectedContentLength = response.expectedContentLength;
      
          	self.recvedfileLength = 0;
      	}
      
      	- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
      
      		// 将数据的 "字节"一次性写入文件流,并且指定数据长度
          	[self.fileStream write:data.bytes maxLength:data.length];
      
      		// 计算当前数据下载完成的大小
          	self.recvedfileLength += data.length;
      
          	// 计算下载进度	
          	float progress = (float)self.recvedfileLength / self.expectedContentLength;
      	}	
      
      	- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
         	
      		// 关闭文件流
          	[self.fileStream close];
      	}
      
      	- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
         
      		// 关闭文件流
          	[self.fileStream close];
      	}
      

    7、NSURLConnection 下载单例封装

    • QDownloaderOperation.h

      	@interface QDownloaderOperation : NSOperation
      
      	/// 类方法
      	+ (instancetype)downloaderWithURL:(NSURL *)url 
      	                         progress:(void (^)(float progress))progress 
      	                        successed:(void (^)(NSString *targetPath))successed 
      	                           failed:(void (^)(NSError *error))failed;
      
      	/// 暂停当前下载
      	- (void)pauseDownload;
      
      	/// 取消当前下载
      	- (void)cancelDownload;
      
      	@end
      
    • QDownloaderOperation.m

      	@interface QDownloaderOperation () <NSURLConnectionDataDelegate>
      
      	/// 下载文件总长度
      	@property (nonatomic, assign) long long expectedContentLength;
      
      	/// 已下载文件大小
      	@property (nonatomic, assign) long long recvedfileLength;
      
      	/// 下载目标目录
      	@property (nonatomic, copy) NSString *targetPath;
      
      	/// 下载文件输出数据流
      	@property (nonatomic, strong) NSOutputStream *fileStream;
      
      	/// block 属性
      	@property (nonatomic, copy) void (^progressBlock)(float);
      	@property (nonatomic, copy) void (^successedBlock)(NSString *);
      	@property (nonatomic, copy) void (^failedBlock)(NSError *);
      
      	/// 网络连接属性
      	@property (nonatomic, strong) NSURLConnection *conn;
      	@property (nonatomic, strong) NSURL *downloadURL;
      
      	@end
      
      	+ (instancetype)downloaderWithURL:(NSURL *)url 
      	                         progress:(void (^)(float))progress 
      	                        successed:(void (^)(NSString *))successed 
      	                           failed:(void (^)(NSError *))failed {
          
          	QDownloaderOperation *downloader = [[self alloc] init];
          
          	downloader.progressBlock = progress;
          	downloader.successedBlock = successed;
          	downloader.failedBlock = failed;
          
          	downloader.downloadURL = url;
          
          	return downloader;
      	}
      
      	- (void)main {
          	@autoreleasepool {
              
             	// 检查服务器上的文件信息
              	[self checkServerFileInfoWithURL:self.downloadURL];
              
              	if (self.isCancelled) return;
              
              	// 检查本地文件信息
              	long long fileSize = [self checkLocalFileInfo];
              
              	if (fileSize == self.expectedContentLength) {
              	
              		// 下载完成的回调
                  	if (self.successedBlock) {
      					dispatch_async(dispatch_get_main_queue(), ^{
      						self.successedBlock(self.targetPath);
      					});
                      
      					// 下载进度的回调
      					if (self.progressBlock) {
      						self.progressBlock(1.0);
      					}
                  	}
                  	return;
              	}
              
              	// 根据本地文件的长度,从对应 "偏移" 位置开始下载
              	[self downloadWithURL:self.downloadURL offset:fileSize];
          	}
      	}
      
      	- (void)pauseDownload {
          
          	// 取消一个请求,调用此方法后,连接不在调用代理方法
          	[self.conn cancel];
      	}
      
      	- (void)cancelDownload {
          
          	[self.conn cancel];
          	[[NSFileManager defaultManager] removeItemAtPath:self.targetPath error:NULL];
      	}
      
      	/// 检查服务器上的文件信息
      
      	- (void)checkServerFileInfoWithURL:(NSURL *)url {
          
          	NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
          	request.HTTPMethod = @"HEAD";
          
          	NSURLResponse *response = nil;
          	NSError *error = nil;
          	
          	// 发送同步方法
          	[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
          
          	if (error == nil && response != nil) {
              
              	// 文件大小
              	self.expectedContentLength = response.expectedContentLength;
              	
              	// 文件名
              	self.targetPath = [NSTemporaryDirectory() stringByAppendingPathComponent:response.suggestedFilename];
          	}
      	}
      
      	/// 检查本地文件信息
      
      	- (long long)checkLocalFileInfo {
          
          	long long fileSize = 0;
          
          	// 检查本地是否存在文件
          	if ([[NSFileManager defaultManager] fileExistsAtPath:self.targetPath]) {
              	NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:self.targetPath error:NULL];
              
              	// 获取文件大小
              	fileSize = [dict fileSize];
          	}
          
          	// 判断是否比服务器的文件大
          	if (fileSize > self.expectedContentLength) {
          	
          		// 删除文件
              	[[NSFileManager defaultManager] removeItemAtPath:self.targetPath error:NULL];
              	fileSize = 0;
          	}
          
          	return fileSize;
      	}
      
      	/// 从断点处开始下载
      
      	- (void)downloadWithURL:(NSURL *)url offset:(long long)offset {
          
          	// 记录本地文件大小
          	self.recvedfileLength = offset;
          
          	NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:1 timeoutInterval:15];
          
          	// 一旦设置了 Range,response 的状态码会变成 206
          	[urlRequest setValue:[NSString stringWithFormat:@"bytes=%lld-", offset] forHTTPHeaderField:@"Range"];
          
          	// 遵守协议 <NSURLConnectionDataDelegate>
          	self.conn = [NSURLConnection connectionWithRequest:urlRequest delegate:self];
          
          	[self.conn start];
           
           	// 开启子线程运行循环
          	CFRunLoopRun();
      	}
      
      	/// 协议方法
      
      	- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
      
      		// 以拼接的方式实例化文件流
          	self.fileStream = [[NSOutputStream alloc] initToFileAtPath:self.targetPath append:YES];
      
          	// 打开文件流
          	[self.fileStream open];
      	}
      
      	- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
      
      		// 将数据的 "字节" 一次性写入文件流,并且指定数据长度
          	[self.fileStream write:data.bytes maxLength:data.length];
      
      		// 计算当前数据下载完成的大小
          	self.recvedfileLength += data.length;
          	
          	// 计算下载进度
          	float progress = (float)self.recvedfileLength / self.expectedContentLength;
          
          	// 进度的回调
          	if (self.progressBlock) {
              	self.progressBlock(progress);
          	}
      	}
      
      	- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
      
      		// 关闭文件流
          	[self.fileStream close];
          
          	// 完成的回调
          	if (self.successedBlock) {
          	
          		// 主线程回调
              	dispatch_async(dispatch_get_main_queue(), ^{
      				self.successedBlock(self.targetPath);
              	});
          	}
      	}
      
      	- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
      
      		// 关闭文件流
      		[self.fileStream close];
          
          	// 失败的回调
          	if (self.failedBlock) {
              	self.failedBlock(error);
          	}
      	}
      
    • QDownloaderManager.h

      	@interface QDownloaderManager : NSObject
      
      	/// 单例
      	+ (instancetype)sharedManager;
      
      	/// 开始下载
      	- (void)downloadWithURL:(NSURL *)url progress:(void (^)(float progress))progress 
      	                                    successed:(void (^)(NSString *targetPath))successed 
      	                                       failed:(void (^)(NSError *error))failed;
      
      	/// 暂停下载
      	- (void)pauseWithURL:(NSURL *)url;
      
      	/// 取消下载
      	- (void)cancelWithURL:(NSURL *)url;
      
      	@end
      
    • QDownloaderManager.m

      	@interface QDownloaderManager ()
      
      	/// 下载操作缓冲池
      	@property (nonatomic, strong) NSMutableDictionary *downloadCache;
      
      	/// 下载操作队列
      	@property (nonatomic, strong) NSOperationQueue *downloadQueue;
      
      	@end
      
      	+ (instancetype)sharedManager {
          	static id instance;
          
          	static dispatch_once_t onceToken;
          	dispatch_once(&onceToken, ^{
              	instance = [[self alloc] init];
          	});
          
          	return instance;
      	}
      
      	- (void)downloadWithURL:(NSURL *)url progress:(void (^)(float))progress 
      	                                    successed:(void (^)(NSString *))successed 
      	                                       failed:(void (^)(NSError *))failed {
          
          	// 检查缓冲池中是否有下载,如果有,直接返回
          	if (self.downloadCache[url.absoluteString] != nil) {
              	NSLog(@"正在在玩命下载中...");
              	return;
          	}
          
          	QDownloaderOperation *downloader = [QDownloaderOperation downloaderWithURL:url 
          	                                                                  progress:progress 
          	                                                                 successed:^(NSString *targetPath) {
              
              	// 删除下载操作
              	[self.downloadCache removeObjectForKey:url.absoluteString];
          
              	if (successed != nil) {
                  	successed(targetPath);
              	}
              
          	} failed:^(NSError *error) {
              
              	[self.downloadCache removeObjectForKey:url.absoluteString];
              
              	if (failed != nil) {
                  	failed(error);
              	}
          	}];
          
          	// 添加到缓冲池
          	[self.downloadCache setObject:downloader forKey:url.absoluteString];
          	
          	// 添加到队列
          	[self.downloadQueue addOperation:downloader];
      	}
      
      	- (void)pauseWithURL:(NSURL *)url {
          
          	// 判断缓冲池中是否有对应的下载操作
          	QDownloaderOperation *downloader = self.downloadCache[url.absoluteString];
          
          	if (downloader != nil) {
              
              	// 暂停 downloader 内部的 NSURLConnection
              	[downloader pauseDownload];
              	
              	// 给操作发送取消消息 NSOperation
              	[downloader cancel];
              
              	// 从缓冲池中清除
              	[self.downloadCache removeObjectForKey:url.absoluteString];
          	}
      	}
      
      	- (void)cancelWithURL:(NSURL *)url {
          
          	// 判断缓冲池中是否有对应的下载操作
          	QDownloaderOperation *downloader = self.downloadCache[url.absoluteString];
          
          	if (downloader != nil) {
              
              	// 取消 downloader 内部的 NSURLConnection
              	[downloader cancelDownload];
              	
              	// 给操作发送取消消息 NSOperation
              	[downloader cancel];
              
              	// 从缓冲池中清除
              	[self.downloadCache removeObjectForKey:url.absoluteString];
          	}
      	}
      
      	/// 懒加载
      
      	- (NSMutableDictionary *)downloadCache {
          	if (_downloadCache == nil) {
              	_downloadCache = [NSMutableDictionary dictionary];
          	}
          	return _downloadCache;
      	}
      
      	- (NSOperationQueue *)downloadQueue {
          	if (_downloadQueue == nil) {
              	_downloadQueue = [[NSOperationQueue alloc] init];
              
              	// 设置最大并发数
              	_downloadQueue.maxConcurrentOperationCount = 5;
          	}
          	return _downloadQueue;
      	}
      
    • ViewController.m

      	// 下载进度
      	@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
      
      	@property (nonatomic, strong) NSURL *url;
      
      	// 开始下载
       
          	NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
          	self.url = url;
          
          	[[QDownloaderManager sharedManager] downloadWithURL:url progress:^(float progress) {
              
              	dispatch_async(dispatch_get_main_queue(), ^{
                  
               		self.progressView.progress = progress;
              	});
              
          	} successed:^(NSString *targetPath) {
              
              	NSLog(@"%@", targetPath);
              
          	} failed:^(NSError *error) {
              
              	NSLog(@"%@", error);
          	}];
      
      	// 暂停下载
      
          	[[QDownloaderManager sharedManager] pauseWithURL:self.url];
      
      	// 取消下载
       
          	[[QDownloaderManager sharedManager] cancelWithURL:self.url];
      
  • 相关阅读:
    java Boolean和boolean的区别
    stack.isEmpty()和empty()
    mysql中文、英文别名排序问题,order by 关键字详解
    数组中array==null和array.length==0的区别
    7.9总结
    7.8总结
    6.25总结
    6.27总结
    6.29总结
    6.28总结
  • 原文地址:https://www.cnblogs.com/QianChia/p/5766535.html
Copyright © 2011-2022 走看看