iosnsurlconnection网络请求

yjand615 贡献于2016-11-25

作者 杨十郎  创建于2016-11-08 09:40:00   修改者杨十郎  修改于2016-11-08 09:40:00字数27078

文档摘要:
关键词:

iOS - NSURLConnection 网络请求 本文目录 · 前言 · 1、NSURLConnection · 2、NSURLConnection 同步 GET 请求 · 3、NSURLConnection 同步 POST 请求 · 4、NSURLConnection 异步 GET 请求 · 5、NSURLConnection 异步 POST 请求 · 6、NSURLConnection 文件下载 · 7、NSURLConnection 下载单例封装 回到顶部 前言 @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,所以使用 协议。 · · 当接收到服务器的响应(连通了服务器)时会调用: · - (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) { · · } }]; · 使用 协议 方式 · // 遵守协议 · · @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) { · · } }]; · 使用 协议 方式 · // 遵守协议 · · // 设置网络接口 · 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 · } · 使用 协议 方式 · // 遵守协议 · · // 设置网络接口 · 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]; · · // 遵守协议 · [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]; · · // 遵守协议 · [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]; · · // 遵守协议 · [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"]; · · // 遵守协议 · 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]; · · // 遵守协议 · [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 () · · /// 下载文件总长度 · @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"]; · · // 遵守协议 · 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];

下载文档到电脑,查找使用更方便

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享文档获得金币 ] 0 人已下载

下载文档