前言
-
Alamofire 是 Swift 语言的 HTTP 网络开发工具包,相当于 Swift 实现 AFNetworking 版本。当然,AFNetworking 非常稳定,在 Mac OSX 与 iOS 中也能像其他 Objective-C 代码一样用 Swift 编写。不过 Alamofire 更适合 Swift 语言风格习惯(Alamofire 与 AFNetworking 可以共存一个项目中,互不影响)。Alamofire 取名来源于 Alamo Fire flower。
-
Alamofire 的核心主要是试图简化 iOS 中 HTTP 网络连接,它通过使用 NSURLSession 以及 Foundation URL Loading System 来创建一个 Swift 本地的网络访问接口,从而实现令人难以置信效率的任务。
1、Alamofire
-
Alamofire 功能:
- Chainable Request / Response methods
- URL / JSON / plist Parameter Encoding
- Upload File / Data / Stream
- Download using Request or Resume data
- Authentication with NSURLCredential
- Progress Closure & NSProgress
- cURL Debug Output
-
Alamofire 系统需求:
Alamofire Version | Minimum iOS Target | Target Notes
------------------------|--------------------------|-------------------------------------------------------------------
3.4.x | iOS 8.0+ | Xcode 7.3+ is required.
3.1.4 -> 3.3.1 | iOS 8.0+ | Xcode 7.2+ is required.
3.1.0 -> 3.1.3 | iOS 8.0+ | Xcode 7.1+ is required.
2.0.0 -> 3.0.1 | iOS 8.0+ | Xcode 7.0+ is required.
1.3.0 -> 1.3.1 | iOS 7.0+ | Xcode 6.4 is required.
1.2.1 -> 1.2.3 | iOS 7.0+ | Xcode 6.3 is required.
1.1.0 -> 1.2.0 | iOS 7.0+ | Xcode 6.1 is required.
1.0.0 -> 1.0.1 | iOS 7.0+ | Xcode 6.0 is required. For Xcode 6.1, use the xcode-6.1 branch. -
Alamofire 有许多让程序猿信服去使用它的理由。在 iOS 开发中,使用 NURLSession 是 HTTP 网络的未来趋势, 相比 NSURLConnection 来说,它的功能更加丰富:
- 后台上传和下载
- 暂停以及重新开始网络操作的能力
- 可配置的容器(Container)
- 子类和私有存储
- 改进的认证处理
- 对每个基础连接进行身份验证
- 多种代理模式 -- NSURLConnection 拥有异步代码块的基本方法, 但是不能用它们的代理,NSURLSession 具有一种混合型的方法。
-
对 AFNetworking 能做而 Alamofire 不能做的有以下几点:
- UIKit 扩展
- TLS 验证
- NSOperation/NSURLConnection/AFURLConnectionOperation 调用
- 多重 HTTP 网络请求构架
2、Alamofire 的添加
-
Github 网址:https://github.com/Alamofire/Alamofire
-
Alamofire 使用 ARC
-
Swift
// 将第三方库文件复制到工程目录下 Alamofire // 将第三方库文件中的 xcodeproj 添加到工程中 Alamofire.xcodeproj // 在 TARGETS -> General -> Embedded Binaries 下添加静态库文件(添加上边的) Alamofire.framework // 添加头文件 import Alamofire
3、Alamofire 的设置
-
Swift
-
请求超时时间设置
// 必须设置为全局的 var alamofireManager: Manager! let config = NSURLSessionConfiguration.defaultSessionConfiguration() config.timeoutIntervalForRequest = 5 // 秒 self.alamofireManager = Manager(configuration: config) self.alamofireManager.request(.GET, "http://120.25.226.186:32812/video?type=JSON")
-
HTTP 方法(Medthods)
Alamofire.Method enum 列表出在 RFC 2616 中定义的 HTTP 方法: public enum Method: String { case OPTIONS = "OPTIONS" case GET = "GET" case HEAD = "HEAD" case POST = "POST" case PUT = "PUT" case PATCH = "PATCH" case DELETE = "DELETE" case TRACE = "TRACE" case CONNECT = "CONNECT" } 这些值可以作为 Alamofire.request 请求的第一个参数。 Alamofire.request(.POST, "https://httpbin.org/post") Alamofire.request(.PUT, "https://httpbin.org/put") Alamofire.request(.DELETE, "https://httpbin.org/delete")
-
请求参数编码方式设置
Alamofire 使用 Alamofire.ParameterEncoding 可以支持 URL query/URI form,JSON,PropertyList 方式编码参数。 enum ParameterEncoding { case URL case URLEncodedInURL case JSON case PropertyList(NSPropertyListFormat, NSPropertyListWriteOptions) case Custom((URLRequestConvertible, [String : AnyObject]?) -> (NSMutableURLRequest, NSError?)) public func encode(URLRequest: URLRequestConvertible, parameters: [String : AnyObject]?) -> (NSMutableURLRequest, NSError?) public func queryComponents(key: String, _ value: AnyObject) -> [(String, String)] public func escape(string: String) -> String } // URL 形式参数编码 // 发送以下 HttpBody 内容: foo=bar&baz[]=a&baz[]=1&qux[x]=1&qux[y]=2&qux[z]=3 let urlStr:URLStringConvertible = "https://httpbin.org/post" let parameters:[String: AnyObject]? = ["foo":"bar", "baz":["a", 1], "qux":["x":1, "y":2, "z":3]] Alamofire.request(.POST, urlStr, parameters: parameters) // 默认编码方式是 URL Alamofire.request(.POST, urlStr, parameters: parameters, encoding: .URL) // JSON 形式参数编码 // 发送以下 HttpBody 内容: {"foo":"bar", "baz":["a", 1], "qux":{"x":1, "y":2, "z":3}} let urlStr:URLStringConvertible = "https://httpbin.org/post" let parameters:[String: AnyObject]? = ["foo":"bar", "baz":["a", 1], "qux":["x":1, "y":2, "z":3]] Alamofire.request(.POST, urlStr, parameters: parameters, encoding:.JSON) // URLRequest 请求编码 let url = NSURL(string: "https://httpbin.org/get")! var urlRequest = NSMutableURLRequest(URL: url) let param = ["foo": "bar"] let encoding = Alamofire.ParameterEncoding.URL (urlRequest, _) = encoding.encode(urlRequest, parameters: param)
-
请求头设置
let headers = ["User-Agent":"iPhone 6s Plus"] Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON", headers: headers) // 不设置时为默认值 Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON")
-
请求数据响应格式设置
Built-in Response Methods: response() responseData() responseString(encoding:NSStringEncoding) responseJSON(options:NSJSONReadingOptions) responsePropertyList(options:NSPropertyListReadOptions) 可以同时响应多种格式数据。 // 响应 NSData 格式数据 Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON") .response { (request:NSURLRequest?, response:NSHTTPURLResponse?, responseData:NSData?, error:NSError?) in /* 网络请求结束,成功时 error == nil。请求返回的数据存在 responseData 中,为 NSData 格式。 */ } // 响应 String 格式数据 Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON") .responseString { (response:Response<String, NSError>) in /* 网络请求结束,成功时 response.result.error == nil。请求返回的数据存在 response.result.value 中,为 String 格式。 或者成功时 response.result 的值为 .Success,失败时 response.result 的值为 .Failure。 */ response.request // original URL request response.response // URL response response.data // server data response.result // result of response serialization // 获取并判断结果值 if let string = response.result.value { } else { } // 判断结果值 if response.result.error == nil { } else { } // 判断结果值 switch response.result { case.Success(let value): case.Failure(let error): } } // 响应 JSON 格式数据 Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON") .responseJSON { (response:Response<AnyObject, NSError>) in /* 网络请求结束,成功时 response.result.error == nil。请求返回的数据存在 response.result.value 中,为 JSON 格式。 或者成功时 response.result 的值为 .Success,失败时 response.result 的值为 .Failure。 */ } // 响应 PList 格式数据 Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON") .responsePropertyList { (response:Response<AnyObject, NSError>) in /* 网络请求结束,成功时 response.result.error == nil。请求返回的数据存在 response.result.value 中,为 PList 格式。 或者成功时 response.result 的值为 .Success,失败时 response.result 的值为 .Failure。 */ } // 响应 多种格式 数据 Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON") // 参数不使用时可以省略 .response { (_, _, responseData:NSData?, error:NSError?) in } .responseJSON { (response:Response<AnyObject, NSError>) in }
-
Request 请求创建方式
// Manager 方式 // 必须设置为全局的 var alamofireManager: Manager! self.alamofireManager = Manager(configuration: NSURLSessionConfiguration.defaultSessionConfiguration()) self.alamofireManager.request(.GET, "http://120.25.226.186:32812/video?type=JSON") // Alamofire 方式,接收返回值 let request = Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON") // 不接收返回值,request 不带参数 Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON") // request 带参数 Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video", parameters: ["type": "JSON"]) // request 带参数及参数编码方式 Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video", parameters: ["type": "JSON"], encoding: .URL) // request 带参数及请求头 Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video", parameters: ["type": "JSON"], encoding: .URL, headers: ["User-Agent":"iPhone 6s"])
-
请求任务创建方式
// 数据请求 request (GET/POST) // Alamofire GET 方式 let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video?type=XML" Alamofire.request(.GET, urlStr) // Alamofire POST 方式 let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video" Alamofire.request(.GET, urlStr, parameters: ["type": "XML"]) // Manager GET 方式 // 必须设置为全局的 var alamofireManager: Manager! let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video?type=XML" self.alamofireManager = Manager(configuration: NSURLSessionConfiguration.defaultSessionConfiguration()) self.alamofireManager.request(.GET, urlStr) // Manager POST 方式 // 必须设置为全局的 var alamofireManager: Manager! let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video" self.alamofireManager = Manager(configuration: NSURLSessionConfiguration.defaultSessionConfiguration()) self.alamofireManager.request(.GET, urlStr, parameters: ["type": "XML"]) // 文件下载 download // 指定文件路径方式 Alamofire.download(.GET, "http://120.25.226.186:32812/resources/videos/minion_01.mp4") { (temporaryURL:NSURL, response:NSHTTPURLResponse) -> NSURL in /* 设置文件下载路径,temporaryURL 是沙盒下的文件临时存储路径,下载完成后会被自动删除。response.suggestedFilename 为服务器端文件名。此 block 在子线程中执行。 */ return documentsDirUrl } .progress { (bytesWrite:Int64, totalBytesWrite:Int64, totalBytesExpectedToWrite:Int64) in /* 监听文件下载进度,此 block 在子线程中执行。 */ } .response { (_, _, _, error:NSError?) in /* 网络请求结束,成功时 error == nil。在 Swift 中文件已经存在时,再次相同路径写入会失败。 */ } // 使用默认提供的下载路径方式 Alamofire.download(.GET, "http://120.25.226.186:32812/resources/videos/minion_01.mp4", destination: destination) .progress { (bytesWrite:Int64, totalBytesWrite:Int64, totalBytesExpectedToWrite:Int64) in /* 监听文件下载进度,此 block 在子线程中执行。 */ } .response { (_, _, _, error:NSError?) in /* 网络请求结束,成功时 error == nil。在 Swift 中文件已经存在时,再次相同路径写入会失败。 */ } // 断点续传下载方式 let downloadRequest:Request = Alamofire.download(resumeData: resumeData, destination: { (temporaryURL:NSURL, response:NSHTTPURLResponse) -> NSURL in /* 设置文件下载路径,temporaryURL 是沙盒下的文件临时存储路径,下载完成后会被自动删除。response.suggestedFilename 为服务器端文件名。此 block 在子线程中执行。 */ return documentsDirUrl }) .progress { (bytesWrite:Int64, totalBytesWrite:Int64, totalBytesExpectedToWrite:Int64) in /* 监听文件下载进度,此 block 在子线程中执行。 */ } .response { (_, _, data:NSData?, error:NSError?) in /* 网络请求结束,成功时 error == nil。在 Swift 中文件已经存在时,再次相同路径写入会失败。 */ } // 文件上传 upload // Data 形式上传 Alamofire.upload(.POST, "http://192.168.88.200:8080/MJServer/upload", headers: headers, data: formBody) .progress { (bytesLoad:Int64, totalBytesLoad:Int64, totalBytesExpectedToLoad:Int64) in /* 监听文件上传进度,此 block 在子线程中执行。 */ } .response { (_, _, responseData:NSData?, error:NSError?) in /* 网络请求结束。 */ } // MultipartFormData 形式上传 Alamofire.upload(.POST, "http://192.168.88.200:8080/MJServer/upload", multipartFormData: { (formData:MultipartFormData) in /* 添加参数。第一个参数是需要添加的参数内容值,第二个是后台规定的参数名。 设置上传的文件。第一个参数是需要上传的文件路径,第二个是后台规定的参数名,第三个是上传后服务器端文件名称,第四个是文件类型。 */ }) { (encodingResult:Manager.MultipartFormDataEncodingResult) in /* 数据编码完成。 */ switch encodingResult { // 编码成功 case .Success(let uploadRequest, _, _): uploadRequest .progress { (bytesLoad:Int64, totalBytesLoad:Int64, totalBytesExpectedToLoad:Int64) in /* 监听文件上传进度,此 block 在子线程中执行。 */ } .response { (_, _, responseData:NSData?, error:NSError?) in /* 网络请求结束。 */ } // 编码失败 case .Failure(let error): print(error) } }
-
请求任务设置
// 继续请求任务 downloadRequest.resume() // 暂停请求任务 downloadRequest.suspend() // 取消请求任务 downloadRequest.cancel()
-
文件下载设置
// 设置文件下载路径 let documentsDirUrl:NSURL = NSFileManager.defaultManager() .URLsForDirectory( .DocumentDirectory, inDomains: .UserDomainMask)[0] .URLByAppendingPathComponent(response.suggestedFilename!) if NSFileManager.defaultManager().fileExistsAtPath(documentsDirUrl.path!) { // 移除已经存在的文件,在 Swift 中文件已经存在时,再次相同路径写入会失败 try! NSFileManager.defaultManager().removeItemAtURL(documentsDirUrl) } // 设置文件默认下载路径 let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask) // 监听文件下载进度 bytesWrite // 本次写入的大小 totalBytesWrite // 已经写入的大小 totalBytesExpectedToWrite // 总大小 // 设置下载进度条 let progressNum:NSNumber = NSNumber(float: Float(Double(totalBytesWrite)/Double(totalBytesExpectedToWrite))) self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)), withObject: progressNum, waitUntilDone: true)
-
文件上传设置
// Data 形式上传 let boundary = "myBoundary" // 设置请求头 /* upload task 不会在请求头里添加 content-type (上传数据类型)字段,@"myBoundary" 为请求体边界,参数可以随便设置,但需一致 */ let headers = ["Content-Type":"multipart/form-data; charset=utf-8; boundary=(boundary)"] // 设置请求文件参数 let formBody = NSMutableData() // 参数开始分割线 /* 每个参数开始前都需要加 */ formBody.appendData("--(boundary)".dataUsingEncoding(NSUTF8StringEncoding)!) formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) // 参数 formBody.appendData("Content-Disposition: form-data; name="("username")" ("jhq")" .dataUsingEncoding(NSUTF8StringEncoding)!) // username 是后台规定的参数名,jhq 是需要添加的参数内容值 formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) // 文件开始分割线 /* 每个文件开始前都需要加 */ formBody.appendData("--(boundary)".dataUsingEncoding(NSUTF8StringEncoding)!) formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) // 文件参数名 formBody.appendData("Content-Disposition: form-data; name="("file")"; filename="("test.mp4")"" .dataUsingEncoding(NSUTF8StringEncoding)!) // file 是后台规定的参数名,test.mp4 为上传后服务器端文件名称 formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) // 文件的类型 formBody.appendData("Content-Type: mp4".dataUsingEncoding(NSUTF8StringEncoding)!) formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) // 待上传文件数据 /* 本地待上传的文件路径 */ formBody.appendData(NSData(contentsOfFile: NSBundle.mainBundle().pathForResource("minion", ofType: "mp4")!)!) formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) // 结束分割线标记 formBody.appendData("--(boundary)--".dataUsingEncoding(NSUTF8StringEncoding)!) formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) // 指定文件路径形式上传 /* public func appendBodyPart(fileURL fileURL: NSURL, name: String, fileName: String, mimeType: String); 第一个参数是需要上传的文件路径,第二个是后台规定的参数名,第三个是上传后服务器端文件名称,第四个是文件类型。 */ let fileUrl = NSBundle.mainBundle().URLForResource("HQ_0005", withExtension: "jpg")! formData.appendBodyPart(fileURL: fileUrl, name: "file", fileName: "test.png", mimeType: "image/jpeg") // 指定文件数据形式上传 /* public func appendBodyPart(data data: NSData, name: String, fileName: String, mimeType: String); 第一个参数是需要上传的文件数据,第二个是后台规定的参数名,第三个是上传后服务器端文件名称,第四个是文件类型。 */ let fileData = NSData(contentsOfFile: NSBundle.mainBundle().pathForResource("minion", ofType: "mp4")!) formData.appendBodyPart(data: fileData!, name: "file", fileName: "test.mp4", mimeType: "mp4") // 添加参数 /* public func appendBodyPart(data data: NSData, name: String); 第一个参数是需要添加的参数内容值,第二个是后台规定的参数名。 */ formData.appendBodyPart(data: "jhq".dataUsingEncoding(NSUTF8StringEncoding)!, name: "username") // 监听文件上传进度 bytesLoad // 本次写入的大小 totalBytesLoad // 已经写入的大小 totalBytesExpectedToLoad // 总大小 let progressNum1:NSNumber = NSNumber(float: Float(Double(totalBytesLoad)/Double(totalBytesExpectedToLoad))) self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)), withObject: progressNum1, waitUntilDone: true)
-
4、Alamofire HTTP 认证
-
支持以下几种认证:
- HTTP Basic
- HTTP Digest
- Kerberos
- NTLM
-
Swift
// Http basic 方式认证 let user = "user" let password = "password" Alamofire.request(.GET, "https://httpbin.org/basic-auth/(user)/(password)") .authenticate(user: user, password: password) // NSURLCredential 方式认证 let user = "user" let password = "password" let credential = NSURLCredential(user: user, password: password, persistence: .ForSession) Alamofire.request(.GET, "https://httpbin.org/basic-auth/(user)/(password)") .authenticate(usingCredential: credential) // headers 方式认证 let user = "user" let password = "password" let credentialData = "(user):(password)".dataUsingEncoding(NSUTF8StringEncoding)! let base64Credentials = credentialData.base64EncodedStringWithOptions([]) let headers = ["Authorization": "Basic (base64Credentials)"] Alamofire.request(.GET, "https://httpbin.org/basic-auth/user/password", headers: headers)
5、Alamofire HTTP 响应状态信息识别
-
Swift
-
手动识别
/* Alamofire 还提供了 HTTP 响应状态的判断识别,通过 validate 方法,对于在我们期望之外的 HTTP 响应状态信息, Alamofire 会提供报错信息: */ Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]) .validate(statusCode: 200..<300) .validate(contentType: ["application/json"])
-
自动识别
// validate 方法还提供自动识别机制,我们调用 validate 方法时不传入任何参数,则会自动认为 200…299 的状态吗为正常: Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]) .validate()
-
6、Alamofire Timeline
-
Swift
/* Alamofire collects timings throughout the lifecycle of a Request and creates a Timeline object exposed as a property on a Response. */ Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video", parameters: ["type": "JSON"]) .validate() .responseJSON { response in print(response.timeline) } The above reports the following Timeline info: Latency: 0.428 seconds Request Duration: 0.428 seconds Serialization Duration: 0.001 seconds Total Duration: 0.429 seconds
7、Alamofire 调试打印
-
Swift
// GET print let request = Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON") print(request) // 打印输出 GET http://192.168.88.200:8080/MJServer/video?type=JSON // POST print let request = Alamofire.request(.POST, "http://192.168.88.200:8080/MJServer/video", parameters: ["type":"JSON"]) print(request) // 打印输出 POST http://192.168.88.200:8080/MJServer/video // GET debugprint let request = Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON") debugPrint(request) // 打印输出 curl 信息 $ curl -i -H "User-Agent: SwiftAlamofire/com.qianqianstudio.SwiftAlamofire (1; OS Version 9.3 (Build 13E230))" -H "Accept-Language: zh-Hans-US;q=1.0, en-US;q=0.9" -H "Accept-Encoding: gzip;q=1.0, compress;q=0.5" "http://192.168.88.200:8080/MJServer/video?type=JSON"
8、Alamofire 网络连接状态检查
-
Swift
网络连接状态: public enum NetworkReachabilityStatus { case Unknown 网络状态未知 case NotReachable 无网络连接 case Reachable(Alamofire.NetworkReachabilityManager.ConnectionType) } public enum ConnectionType { case EthernetOrWiFi WiFi 网络 case WWAN 无线网络(蜂窝移动网络) } let manager = NetworkReachabilityManager() // 监听网络状态闭包 manager?.listener = { status in /* 开启网络状态监听后,只要网络状态发生改变就会调用该闭包代码段。 */ print("Network Status Changed: (status)") } // 开启监听网络状态 manager?.startListening() // 关闭网络状态监听 manager?.stopListening() // 获取网络连接状态 let status = manager?.networkReachabilityStatus // 判断网络是否连接 let isReachable:Bool? = manager?.isReachable // 判断 WiFi 是否连接 let isReachableOnEthernetOrWiFi:Bool? = manager?.isReachableOnEthernetOrWiFi // 判断 无线网络 是否连接 let isReachableOnWWAN:Bool? = manager?.isReachableOnWWAN
9、Alamofire 异步 GET 数据请求
-
Swift
// Alamofire 方式 let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video?type=XML" Alamofire.request(.GET, urlStr) .response { (request:NSURLRequest?, response:NSHTTPURLResponse?, responseData:NSData?, error:NSError?) in /* 网络请求结束,成功时 error == nil。请求返回的数据存在 responseData 中,为 NSData 格式。 */ } // Manager 方式 // 必须设置为全局的 var alamofireManager: Manager! let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video?type=XML" self.alamofireManager = Manager(configuration: NSURLSessionConfiguration.defaultSessionConfiguration()) self.alamofireManager.request(.GET, urlStr) .response { (request:NSURLRequest?, response:NSHTTPURLResponse?, responseData:NSData?, error:NSError?) in /* 网络请求结束,成功时 error == nil。请求返回的数据存在 responseData 中,为 NSData 格式。 */ }
10、Alamofire 文件下载
-
支持的类型:
- Request
- Resume Data
-
默认支持后台方式下载
-
Swift
-
指定文件路径方式
// 目标路径闭包展开 Alamofire.download(.GET, "http://120.25.226.186:32812/resources/videos/minion_01.mp4") { (temporaryURL:NSURL, response:NSHTTPURLResponse) -> NSURL in /* 设置文件下载路径,temporaryURL 是沙盒下的文件临时存储路径,下载完成后会被自动删除。response.suggestedFilename 为服务器端文件名。此 block 在子线程中执行。 */ let documentsDirUrl:NSURL = NSFileManager.defaultManager() .URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] .URLByAppendingPathComponent(response.suggestedFilename!) if NSFileManager.defaultManager().fileExistsAtPath(documentsDirUrl.path!) { // 移除已经存在的文件,在 Swift 中文件已经存在时,再次相同路径写入会失败 try! NSFileManager.defaultManager().removeItemAtURL(documentsDirUrl) } return documentsDirUrl } .progress { (bytesWrite:Int64, totalBytesWrite:Int64, totalBytesExpectedToWrite:Int64) in /* 监听文件下载进度,此 block 在子线程中执行。 */ // 设置下载进度条 let progressNum:NSNumber = NSNumber(float: Float(Double(totalBytesWrite)/Double(totalBytesExpectedToWrite))) self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)), withObject: progressNum, waitUntilDone: true) } .response { (_, _, _, error:NSError?) in /* 网络请求结束,成功时 error == nil。在 Swift 中文件已经存在时,再次相同路径写入会失败。 */ }
-
使用默认提供的下载路径方式
// 目标路径闭包 // 设置文件的默认下载路径 let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask) Alamofire.download(.GET, "http://120.25.226.186:32812/resources/videos/minion_01.mp4", destination: destination) .progress { (bytesWrite:Int64, totalBytesWrite:Int64, totalBytesExpectedToWrite:Int64) in /* 监听文件下载进度,此 block 在子线程中执行。 */ // 设置下载进度条 let progressNum:NSNumber = NSNumber(float: Float(Double(totalBytesWrite)/Double(totalBytesExpectedToWrite))) self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)), withObject: progressNum, waitUntilDone: true) } .response { (_, _, _, error:NSError?) in /* 网络请求结束,成功时 error == nil。在 Swift 中文件已经存在时,再次相同路径写入会失败。 */ }
-
断点续传下载方式
// 使用断点下载需要之前下载的临时文件存在,才能继续下载。 var downloadRequest:Request! var resumeData:NSData! // 开始下载 let resumeTmpPath = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)[0] + "/resumeData.tmp" // 判断断点保存的文件是否存在 if NSFileManager.defaultManager().fileExistsAtPath(resumeTmpPath) { // 断点开始下载 // 读取断点保存的数据 self.resumeData = NSData(contentsOfFile: resumeTmpPath) self.downloadRequest = Alamofire.download(resumeData: self.resumeData, destination: { (temporaryURL:NSURL, response:NSHTTPURLResponse) -> NSURL in let documentsDirUrl:NSURL = NSFileManager.defaultManager() .URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] .URLByAppendingPathComponent(response.suggestedFilename!) if NSFileManager.defaultManager().fileExistsAtPath(documentsDirUrl.path!) { try! NSFileManager.defaultManager().removeItemAtURL(documentsDirUrl) } return documentsDirUrl }) .progress { (bytesWrite:Int64, totalBytesWrite:Int64, totalBytesExpectedToWrite:Int64) in let progressNum:NSNumber = NSNumber(float: Float(Double(totalBytesWrite)/Double(totalBytesExpectedToWrite))) self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)), withObject: progressNum, waitUntilDone: true) } .response { (_, _, data:NSData?, error:NSError?) in if error == nil { // 删除断点下载缓存文件 let resumeTmpPath = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)[0] + "/resumeData.tmp" if NSFileManager.defaultManager().fileExistsAtPath(resumeTmpPath) { try! NSFileManager.defaultManager().removeItemAtPath(resumeTmpPath) } } else { // 下载的临时文件不存在处理 if error?.localizedFailureReason == "No such file or directory" { let resumeTmpPath = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)[0] + "/resumeData.tmp" if NSFileManager.defaultManager().fileExistsAtPath(resumeTmpPath) { // 删除断点下载缓存文件,否则继续断点下载会报错 try! NSFileManager.defaultManager().removeItemAtPath(resumeTmpPath) } } } } } else { // 重新开始下载 self.resumeData = NSData() self.downloadRequest = Alamofire.download(.GET, "http://120.25.226.186:32812/resources/videos/minion_01.mp4") { (temporaryURL:NSURL, response: NSHTTPURLResponse) -> NSURL in let documentsDirUrl:NSURL = NSFileManager.defaultManager() .URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] .URLByAppendingPathComponent(response.suggestedFilename!) if NSFileManager.defaultManager().fileExistsAtPath(documentsDirUrl.path!) { try! NSFileManager.defaultManager().removeItemAtURL(documentsDirUrl) } return documentsDirUrl } .progress { (bytesWrite:Int64, totalBytesWrite:Int64, totalBytesExpectedToWrite:Int64) in let progressNum:NSNumber = NSNumber(float: Float(Double(totalBytesWrite)/Double(totalBytesExpectedToWrite))) self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)), withObject: progressNum, waitUntilDone: true) } .response { (_, _, data:NSData?, error:NSError?) in if error == nil { } else { // 停止下载处理 if error!.code == NSURLErrorCancelled { if data != nil { // 意外终止的话,把已下载的数据储存起来 self.resumeData = data let resumeTmpPath = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)[0] + "/resumeData.tmp" self.resumeData.writeToFile(resumeTmpPath, atomically: true) } } else { } } } } // 暂停下载 self.downloadRequest.suspend() // 继续下载 self.downloadRequest.resume() // 停止下载 self.downloadRequest.cancel()
-
11、Alamofire 异步 POST 数据请求
-
Swift
-
Alamofire 方式
let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video" let parameters:[String: AnyObject]? = ["type":"XML"] Alamofire.request(.POST, urlStr, parameters: parameters) .response { (request:NSURLRequest?, response:NSHTTPURLResponse?, responseData:NSData?, error:NSError?) in /* 网络请求结束,成功时 error == nil。请求返回的数据存在 responseData 中,为 NSData 格式。 */ }
-
Manager 方式
// 必须设置为全局的 var alamofireManager: Manager! let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video" let parameters:[String: AnyObject]? = ["type":"XML"] self.alamofireManager = Manager(configuration: NSURLSessionConfiguration.defaultSessionConfiguration()) self.alamofireManager.request(.POST, urlStr, parameters: parameters) .response { (request:NSURLRequest?, response:NSHTTPURLResponse?, responseData:NSData?, error:NSError?) in /* 网络请求结束,成功时 error == nil。请求返回的数据存在 responseData 中,为 NSData 格式。 */ }
-
12、Alamofire 文件上传
-
支持的类型:
- File
- Data
- Stream
- MultipartFormData
-
Swift
-
Data 形式上传
let boundary = "myBoundary" // 设置请求头 /* upload task 不会在请求头里添加 content-type (上传数据类型)字段,@"myBoundary" 为请求体边界,参数可以随便设置,但需一致 */ let headers = ["Content-Type":"multipart/form-data; charset=utf-8; boundary=(boundary)"] // 设置请求文件参数 let formBody = NSMutableData() // 参数开始分割线 /* 每个参数开始前都需要加 */ formBody.appendData("--(boundary)".dataUsingEncoding(NSUTF8StringEncoding)!) formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) // 参数 /* username 是后台规定的参数名,jhq 是需要添加的参数内容值 */ formBody.appendData("Content-Disposition: form-data; name="("username")" ("jhq")" .dataUsingEncoding(NSUTF8StringEncoding)!) formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) // 文件开始分割线 /* 每个文件开始前都需要加 */ formBody.appendData("--(boundary)".dataUsingEncoding(NSUTF8StringEncoding)!) formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) // 文件参数名 /* file 是后台规定的参数名,test.mp4 为上传后服务器端文件名称 */ formBody.appendData("Content-Disposition: form-data; name="("file")"; filename="("test.mp4")"" .dataUsingEncoding(NSUTF8StringEncoding)!) formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) // 文件的类型 formBody.appendData("Content-Type: mp4".dataUsingEncoding(NSUTF8StringEncoding)!) formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) // 待上传文件数据 /* 本地待上传的文件路径 */ formBody.appendData(NSData(contentsOfFile: NSBundle.mainBundle().pathForResource("minion", ofType: "mp4")!)!) formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) // 结束分割线标记 formBody.appendData("--(boundary)--".dataUsingEncoding(NSUTF8StringEncoding)!) formBody.appendData(" ".dataUsingEncoding(NSUTF8StringEncoding)!) Alamofire.upload(.POST, "http://192.168.88.200:8080/MJServer/upload", headers: headers, data: formBody) .progress { (bytesLoad:Int64, totalBytesLoad:Int64, totalBytesExpectedToLoad:Int64) in /* 监听文件上传进度,此 block 在子线程中执行。 */ let progressNum:NSNumber = NSNumber(float: Float(Double(totalBytesLoad)/Double(totalBytesExpectedToLoad))) self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)), withObject: progressNum, waitUntilDone: true) } .response { (_, _, responseData:NSData?, error:NSError?) in /* 网络请求结束。 */ }
-
MultipartFormData 形式上传
Alamofire.upload(.POST, "http://192.168.88.200:8080/MJServer/upload", multipartFormData: { (formData:MultipartFormData) in /* 添加参数。第一个参数是需要添加的参数内容值,第二个是后台规定的参数名。 设置上传的文件。第一个参数是需要上传的文件路径,第二个是后台规定的参数名,第三个是上传后服务器端文件名称,第四个是文件类型。 */ // 添加参数 formData.appendBodyPart(data: "jhq".dataUsingEncoding(NSUTF8StringEncoding)!, name: "username") // 指定文件路径形式上传 let fileUrl = NSBundle.mainBundle().URLForResource("HQ_0005", withExtension: "jpg")! formData.appendBodyPart(fileURL: fileUrl, name: "file", fileName: "test.png", mimeType: "image/jpeg") // 指定文件数据形式上传 let fileData = NSData(contentsOfFile: NSBundle.mainBundle().pathForResource("minion", ofType: "mp4")!) formData.appendBodyPart(data: fileData!, name: "file", fileName: "test.mp4", mimeType: "mp4") }) { (encodingResult:Manager.MultipartFormDataEncodingResult) in /* 数据编码完成。 */ switch encodingResult { // 编码成功 case .Success(let uploadRequest, _, _): uploadRequest .progress { (bytesLoad:Int64, totalBytesLoad:Int64, totalBytesExpectedToLoad:Int64) in /* 监听文件上传进度,此 block 在子线程中执行。 */ let progressNum:NSNumber = NSNumber(float: Float(Double(totalBytesLoad)/Double(totalBytesExpectedToLoad))) self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)), withObject: progressNum, waitUntilDone: true) } .response { (_, _, responseData:NSData?, error:NSError?) in /* 网络请求结束。 */ } // 编码失败 case .Failure(let error): print(error) } }
-