• iOS 网络层次结构 • 基于 iOS 提供 API 实现上传文件和断点续传的思路
• 常用 iOS 第三方网路框架简介 - AFNetworking ( AFN ) - ASIHTTPRequest ( ASI )
• 另外一个常用框架 - SSZipArchive
• ANF 使用演示
目标
• iOS 网络层次结构 • 基于 iOS 提供 API 实现上传文件和断点续传的思路
• 常用 iOS 第三方网路框架简介 - AFNetworking ( AFN ) - ASIHTTPRequest ( ASI )
• 另外一个常用框架 - SSZipArchive
• ANF 使用演示
iOS 网络编程层次结构示意图
Cocoa 层 (NSURL,Bonjour,Game Kit,WebKit)
Core Foundation 层 ( 基于 C 的 CFNetwork 和 CFNetServices)
OS 层 ( 基于 C 的 BSD socket)
iOS 网络编程层次结构示意图
iOS 网络编程层次结构概述
• Cocoa 层 :是最上层的 基于 OC 的 API ,比如 URL 访问, NSStream , Bonjour ,GameKit 等,这是大多数情况下我们常用的 API 。 Cocoa 层是基于 Core Foundation实现的
• Core Foundation 层 : 基于 C 语言的框架 ,因为直接使用 socket 需 要更多的编程工作,所以苹果对 OS 层的 socket 进行简单的封装 以简化编程任务。该层提供了CFNetwork 和 CFNetServices ,其 中 CFNetwork 又是基于 CFStream 和 CFSocket
• OS 层 :最底层的 BSD socket 提供了对网络编程最大程度的控制, 但是编程工作也是最多的。苹果建议我们使用 Core Foundation 及以上层的 API 进行编程。 BSD 是UNIX 系统中通用的网络接口, 它不仅支持各种不同的网络类型,而且也是一种内部进程之间 的通信机制
不使用第三方框架如何实现 文件上传 ?
• 使用 NSURLConnection 无法通过 HTML 表单来上传 图片, 因此想要上传图片,必须实现 HTTP 请求 , 而不能像其他语言那样通过 HTML 表单的 POST 就 能上传
不使用第三方框架如何实现 断点续传 ?
• 要使用 NSURLConnection 实现断点续传
• 需要 自定义 URLRequest 的头部的 Range 属性 ,通知 URLConnection 只是 去获取部分网络内容
• Range 头域示例
• Range 头域可以请求实体的一个或者多个子范围。例如,
• 表示头 500 个字节: bytes=0-499
• 表示第二个 500 字节: bytes=500-999
• 表示最后 500 个字节: bytes=-500
• 表示 500 字节以后的范围: bytes=500-
• 第一个和最后一个字节: bytes=0-0,-1
• 同时指定几个范围: bytes=500-600,601-999
• 但是服务器可以忽略此请求头,如果 无条件 GET 包含 Range 请求头 ,响 应会以状态码 206 ( PartialContent )返回而不是以 200 ( OK )
为什么要选择第三方框架
• 第三方框架把复杂的网络底层操作 封装成友好的类和方法,并且加入 异常处理 等,从而可以:
- 高效的与服务端 API 进行数据交换 - 提高开发效率和稳定性
• 选择第三方框架的原则 1. 是否被广泛使用,有足够多得大牛验证过
2. 尽量保证能够看懂其中的所有代码 3. 可以学习编写其中部分方法, 但是不要去直接修改
常用的 iOS 网络开发框架
• AFNetworking ( AFN )
• ASIHTTPRequest ( ASI ) HTTP 终结者,非 ARC
备注: 2012 年 10 月 ASI 停止更新,但这并不意味着 ASI 的应用已经停止
AFN vs ASI • AFN :
- 官方推荐的使用方法: 为一系列相关的请求定义一个 HTTPClient ,共用一个BaseURL 。每次请求把 URL 中除 BaseURL 的 Path 部分做为参数传给 HTTPClient的静态方法,并注册一个 Block 用于回调
- 基于 NSURL ,性能和稳定性略差
- AFN 只封装了一些常用功能,满足基本需求,而直接忽略了很多扩展功能
- 针对 JSON 、 XML 、 PList 和 Image 四种数据结构封装了各自处理器,开发者可 以把处理器注册到操作队列中,直接在回调方法中获得格式化以后的数据
• ASI :
- 推荐使用方法:每一个请求都由构造方法初始化一个(共享)实例,通过这 个实例配置参数并发起请求。 ASI 最初使用 delegate 模式回调,在 iOS SDK 支持 Block 之后也提供了注册 Block 的实例方法(注: ASI 的 Block 不易使用)
- 基于 CFNetwork ,性能和稳定性略高
- ASI 的扩展功能非常丰富
- ASI 没有针对任何数据类型做特别封装,只是预留了各种接口和工具供开发者 自行扩展
AFN 和 ASI 的选择
• AFN 适合逻辑简单的应用 ,或者更适合开发资源尚不丰富的团队,因 为 AFN 的 易用性要比 ASI 好很多 ,而这样的应用(或团队)对底层网络 控件的定制化要求也非常低。
• ASI 更适合已经发展了一段时间的应用 ,或者开发资源相对丰富的团 队,因为往往这些团队(或他们的应用)已经积累了一定的经验,无 论是产品上还是技术上的。需求复杂度就是在这种时候高起来,而且 底层订制的需求也越来越多,此时 AFN 就很难满足需求,需要牺牲一 定的易用性 ,使用 ASI 作为网络底层控件。
AFNetworking ( AFN ) • 下载地址
https://github.com/AFNetworking/AFNetworking
• AFNetworking 官网地址: http://afnetworking.com
• 新建项目并导入 ANF 框架的步骤 • 演练
新建项目并导入 AFN 框架的步骤
• 1. 将框架程序拖拽进项目 • 2 . 添加 iOS 框架引用
- SystemConfiguration.framework
- MobileCoreServices.framework • 3. 修改 xxx-Prefix.pch 文件
#import #import
AFN--AFHTTPClient
• 1. 使用 baseURL 实例化
• 2. 建立 NSURLRequest
Ø 创建 GET 、 HEAD 、 PUT 、 DELETE 方法请求
² requestWithMethod:path:parameters:
Ø 创建 POST 方法请求
² multipartFormRequestWithMethod:path:parameters: constructingBodyWithBlock:
• 3. 检测网路连接状态 - setReachabilityStatusChangeBlock
AFHttpRequestOperation-- 对 NSURLConnection 的封装
• AFHttpRequestOperation HTTP 请求操作 Ø AFJSONRequestOperation 对 JSON请求的封装 Ø AFXMLRequestOperation 对 XML 请求的封装 ØAFPropertyListRequestOperation 对 Plist 请求的封装 Ø AFImageRequestOperation对图像请求的封装
• 块代码操作 Ø setCompletionBlockWithSuccess 设置请求完成块代码
Ø setUploadProgressBlock 设置上传进度块代码 Ø setDownloadProgressBlock 设置下载进度块代码
• 下载操作需要设置 outputStream
Ø 针对请求的操作 pause (暂停) resume (继续)
检测网络连接状态
AFHTTPClient *client = [ AFHTTPClient clientWithBaseURL :[ NSURL URLWithString: @" http://www.baidu.com " ]];
[client setReachabilityStatusChangeBlock :^( AFNetworkReachabilityStatus status) { }];
加载 JSON & XML
AFJSONRequestOperation *op = [ AFJSONRequestOperationJSONRequestOperationWithRequest :request success :^( NSURLRequest *request,NSHTTPURLResponse *response, id JSON) {
NSLog ( @"%@" , JSON);
} failure :^( NSURLRequest *request, NSHTTPURLResponse *response, NSError*error, id
JSON) {
NSLog ( @"%@" , JSON);
}];
上传图像
NSURLRequest *request = [client multipartFormRequestWithMethod : @"POST"path : @"/~liufan9/ itcast/upload.php" parameters : nil constructingBodyWithBlock:^( id < AFMultipartFormData > formData) {
[formData appendPartWithFileData :imageData name : @"file" fileName :@"update.png" mimeType : @"image/png" ];
}];
// 定义操作 AFHTTPRequestOperation *operation = [[ AFHTTPRequestOperation
alloc ] initWithRequest :request]; // 设置上传 // 设置下载进度
演练( 6 ) -- 断点续传 • 设置操作的 输出流 (在网络中的数据是流的方式传输)
• [operation setOutputStream :[ NSOutputStream outputStreamToFileAtPath:downloadPath append : YES ]];
解压缩 -- 另一个第三方框架 SSZipArchive
下载地址: https://github.com/samsoffes/ssziparchive
注意: 需要引入 libz.dylib 框架
// Unzipping
NSString * zipPath = @"path_to_your_zip_file" ;
NSString * destinationPath = @"path_to_the_folder_where_you_want_it_unzipped" ; [SSZipArchive unzipFileAtPath : zipPath toDestination : destinationPath];
// Zipping
NSString * zippedPath = @"path_where_you_want_the_file_created" ; NSArray *inputPaths = [NSArray arrayWithObjects :
[[NSBundle mainBundle] pathForResource : @"photo1" ofType : @"jpg" ], [[NSBundle mainBundle] pathForResource : @"photo2" ofType : @"jpg" ] nil ];
[SSZipArchive createZipFileAtPath : zippedPath withFilesAtPaths : inputPaths];
ASIHTTPRequest ( ASI )
• 下载地址
http://github.com/pokeb/asi-http-request/tarball/master
• 新建项目并导入 ASI 框架的步骤 • 演练:
- 从服务器下载文件并解压缩
使用 ASI 的两点注意事项
• ASI 框架是 不支持 ARC 的
• ASI 框架是 基于i OS5.0 的,如果选择i OS6.0 会有一些苹果官方不再维护 的方法
新建项目并导入 ASI 框架的步骤( 1 )
• 新建项目, 不选择 ARC 支持 • 将 Deployment Target 修改为 5.0
• 将 ASI 解压缩包中的以下两个文件夹拖入项目
- Classes - External
新建项目并导入 ASI 框架的步骤( 2 )
• 编译出现错误: ASIWebPageRequest.m:13:9: 'libxml/HTMLparser.h' file not found
• 错误原因: - 无法正确找到 libxml/HTMLparser.h
• 解决方法: - 在头文件搜索目录中添加: ${SDK_DIR}/usr/include/libxml2
添加搜索路径示意图
新建项目并导入 ASI 框架的步骤( 3 ) • 编译出现错误:
ASITestCase.h:12:9: 'GHUnitIOS/GHUnit.h' file not found
• 错误原因: 没有 GHUnit 框架 • 解决办法: 删除单元测试部分的代码引用
删除单元测试代码引用示意图
新建项目并导入 ASI 框架的步骤( 4 ) • 编译:
85 个错误
瞬间凌乱有木有 ~~~
新建项目并导入 ASI 框架的步骤( 4 )
• 解决方法:添加以下 iOS 框架引用 - CFNetwork.framework
- SystemConfiguration.framework - MobileCoreServices.framework - libz.dylib
- libxml2.dylib
• 怎么知道需要这些框架?
编译通过
ASI 断点续传实现思路回顾
• 目标:使用断点续传的方式下载 zip 文件并解压缩 • 思路:
1. 指定下载文件地址 2. 设定文件保存路径及缓存路径 3. 创建 ASIHTTPRequest
4. 设置 代理 --ASI 是通过代理回调的方式处理网络请求的 5. 设置下载路径 6. 设置缓存路径 7. 设置断点续传 8. 设置下载进程 代理 -- 用户想知道下载的实际进展情况 9. 启动异步请求
ASI 下载文件的准备工作 -- 源和目标
// 1. 指定下载文件地址
NSString *string = @" http://localhost/~liufan9/itcast/download/iTunesConnect_DeveloperGuide_CN.zip" ;
NSURL *url = [ NSURL URLWithString :string];
// 2. 设定文件保存路径及缓存路径
NSArray *documents = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory , NSUserDomainMask , YES );
NSString *downloadPath = [documents[ 0 ] stringByAppendingPathComponent :@"book.zip" ];
NSString *tempPath = [documents[ 0 ] stringByAppendingPathComponent :@"book.tmp" ];
ASI 下载文件请求定义部分代码
// 3. 创建 ASIHTTPRequest ASIHTTPRequest *request = [ ASIHTTPRequestrequestWithURL :url]; // 4. 设置代理 --ASI 是通过代理回调的方式处理网络请求的[request setDelegate : self ];
// 5. 设置下载路径 [request setDownloadDestinationPath :downloadPath];
// 6. 设置缓存路径 [request setTemporaryFileDownloadPath :tempPath];
// 7. 设置断点续传 [request setAllowResumeForFileDownloads : YES ];
// 8. 设置下载进程代理 [request setDownloadProgressDelegate : self ];
// 9. 启动异步请求 -- 用户想知道下载的实际进展情况 [request start ];
NSURLConnectionDataDelegate 的代理方法 回顾
// 服务器开始返回数据 - ( void )connection:didReceiveResponse: // 收到服务器返回的数据, 本方法会被调用多次 - ( void )connection:didReceiveData:
// 数据接收完毕 - ( void )connectionDidFinishLoading:
// 网络连接错误 - ( void )connection:didFailWithError: // 发送数据给服务器, POST 请求使用此方法
- ( void )connection:didSendBodyData:totalBytesWritten: totalBytesExpectedToWrite:
ASIRequest 代理方法
// 请求开始
- ( void )requestStarted:( ASIHTTPRequest *)request
// 请求接收到响应的头部,包括 文件大小信息
- ( void )request:( ASIHTTPRequest *)request didReceiveResponseHeaders: (NSDictionary *)responseHeaders
// 请求完成 - ( void )requestFinished:( ASIHTTPRequest *)request
// 请求失败 - ( void )requestFailed:( ASIHTTPRequest *)request 对比结果: 1.ASIRequest 不需要处理中间数据 2. 但是请求开始拆分成了两部分
实现代理方法之前需要先遵从代理协议
1. ASIHTTPRequestDelegate 2. ASIProgressDelegate
ASIRequest 代理方法实现( 1 )
• 给四个代理方法添加 NSLog ,看看都在干什么 • 目的:运行根据 Log 结果写思路 • 请求头部可以看到文件长度,思路如下:
// 2. 发现其中有一个 "Content-Length" = 6105204; // 貌似是和文件下载进度有关的工作可以在这里进行 // 暂时先放一下
• 请求完成: // 需求:
// 1. 知道文件保存路径 // 2. 解压缩文件 // 3. 删除压缩文件
下一目标解压缩文件
运行看看文件来了吗?
解压缩 -- 另一个第三方框架 SSZipArchive 下载地址:https://github.com/samsoffes/ssziparchive
注意: 需要引入 libz.dylib 框架
// Unzipping
NSString * zipPath = @"path_to_your_zip_file" ;
NSString * destinationPath = @"path_to_the_folder_where_you_want_it_unzipped" ; [SSZipArchive unzipFileAtPath : zipPath toDestination : destinationPath];
// Zipping
NSString * zippedPath = @"path_where_you_want_the_file_created" ; NSArray *inputPaths = [NSArray arrayWithObjects :
[[NSBundle mainBundle] pathForResource : @"photo1" ofType : @"jpg" ], [[NSBundle mainBundle] pathForResource : @"photo2" ofType : @"jpg" ] nil ];
[SSZipArchive createZipFileAtPath : zippedPath withFilesAtPaths : inputPaths];
请求完成部分代码实现
NSLog ( @" 请求完成 " );
// 需求: // 1. 知道文件保存路径(运行,发现文件已经下载完成了) // 2. 解压缩文件(导入SSZipArchive 框架) // 根据 SSZipArchive 框架用法写思路 // 2.1 设置压缩文件路径
NSArray *documents = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory , NSUserDomainMask , YES );
NSString *downloadPath = [documents[ 0 ] stringByAppendingPathComponent :@"book.zip" ];
// 2.2 设置解压缩文件路径,保存在当前路径 NSString *unzipPath = documents[ 0 ];
// 2.3 解压缩 [ SSZipArchive unzipFileAtPath :downloadPath toDestination:unzipPath];
// 3. 删除压缩文件
[[ NSFileManager defaultManager ] removeItemAtPath :downloadPath error : nil ];
下载进度跟踪 -- ASIProgressDelegate 下一目标
ASIProgressDelegate--setProgress
#pragma mark - 下载进度代理方法 - ( void )setProgress:( float )newProgress {
// 通过 Log 发现传入的是一个百分比的数组 // 现在需要一个文件大小,并提示用户文件的大小
NSLog ( @"%f" , newProgress); }
ASIRequest 响应头部的代码实现
1. ASIRequest 响应头部的代码实现
// 1. NSLOG 看看头部是什么内容 NSLog ( @"%@" , responseHeaders);
// 2. 发现其中有一个 "Content-Length" = 6105204; // 貌似是和文件下载进度有关的工作可以在这里进行 // 将文件大小转换成 M 为单位 字节 -K-M _fileLength = request.contentLength / 1024.0 / 1024.0 ; NSLog ( @"%.2fM" , _fileLength );
2. setProgress 方法 NSLog ( @"%.2f" , newProgress * _fileLength );
ASI 框架小结
• ASI 框架是不支持 ARC 的
• ASI 框架是基于 iOS5.0 的
• 使用前的准备工作有点繁琐
• 目前有不少的公司在使用 ASI 框架,不过大多新的项目已经转用 AFN 框 架
• 2012 年 10 月 ASI 停止更新,但这并不意味着 ASI 的应用已经停止
• 学习也是需求驱动的,知道了 ASI 框架的基础用法后,今后工作中如 果需要,可以针对具体知识点去谷歌 + 度娘
关于第三方框架的小结
• 第三方框架把复杂的网络底层操作 封装成友好的类和方法,并且加入异常处 理 等,从而可以:
- 高效的与服务端 API 进行数据交换
- 提高开发效率和稳定性 • 选择第三方框架的原则
1. 尽量保证能够看懂其中的所有代码 2. 是否被广泛使用 3. 可以学习编写其中部分方法,但是不要去直接修改
常用的 iOS 网络开发框架 - AFN 适合逻辑简单的应用, 基于 NSURL
- ASI 更适合已经发展了一段时间的应用,基于 CFNetwork 已停止更新
不要视图完全吃透了再去使用,可以针对具体知识点去谷歌 + 度娘