Swift - 网络抽象层库Moya的使用详解4(单文件上传:文件流方式)
作者:hangge | 2017-10-09 08:10
下面接着介绍如何通过 Moya 上传文件。本文首先演示如何以文件流的形式上传文件。注意这种方式一次只能上传一个文件。
六、文件流的形式上传文件
1,基本用法
(1)网络层定义(MyServiceAPI.swift)
只要在 task 属性中,将需要上传的文件 URL 通过 .uploadFile() 返回即可。
import Moya //初始化请求的provider let MyServiceProvider = MoyaProvider<MyService>() //请求分类 public enum MyService { case upload(fileURL:URL) //上传文件 } //请求配置 extension MyService: TargetType { //服务器地址 public var baseURL: URL { return URL(string: "http://www.hangge.com")! } //各个请求的具体路径 public var path: String { return "/upload.php" } //请求类型 public var method: Moya.Method { return .post } //请求任务事件(这里附带上参数) public var task: Task { switch self { case let .upload(fileURL): return .uploadFile(fileURL) } } //是否执行Alamofire验证 public var validate: Bool { return false } //这个就是做单元测试模拟的数据,只会在单元测试文件中有作用 public var sampleData: Data { return "{}".data(using: String.Encoding.utf8)! } //请求头 public var headers: [String: String]? { return nil } }
(2)使用样例
//需要上传的文件 let fileURL = Bundle.main.url(forResource: "hangge", withExtension: "zip")! //通过Moya提交数据 MyServiceProvider.request(.upload(fileURL: fileURL)) { result in if case let .success(response) = result { //解析数据 let data = try? response.mapString() print(data ?? "") } }
(3)服务端代码(upload.php)
<?php /** php 接收流文件 * @param String $file 接收后保存的文件名 * @return boolean */ function receiveStreamFile($receiveFile){ $streamData = isset($GLOBALS['HTTP_RAW_POST_DATA'])? $GLOBALS['HTTP_RAW_POST_DATA'] : ''; if(empty($streamData)){ $streamData = file_get_contents('php://input'); } if($streamData!=''){ $ret = file_put_contents($receiveFile, $streamData, true); }else{ $ret = false; } return $ret; } //定义服务器存储路径和文件名 $receiveFile = $_SERVER["DOCUMENT_ROOT"]."/file/hangge.zip"; $ret = receiveStreamFile($receiveFile); echo json_encode(array('success'=>(bool)$ret)); ?>
(4)上传完毕后控制台输出如下:
2,实时获取上传进度
(1)通过 progress 回调函数,我们可以实时得到当前的上传进度。
//需要上传的文件 let fileURL = Bundle.main.url(forResource: "hangge", withExtension: "zip")! //通过Moya提交数据 MyServiceProvider.request(.upload(fileURL: fileURL), progress:{ progress in //实时打印出上传进度 print("当前进度: \(progress.progress)") }) { result in if case let .success(response) = result { //解析数据 let data = try? response.mapString() print(data ?? "") } }
(2)可以看到控制台不断输出已上传的进度(1 表示上传完毕):
3,在上传时附带上文件名
有时我们在文件上传的同时还会想要附带一些其它参数,比如文件名。这样服务端接收到文件后,就可以根据我们传过来的文件名来保存。
(1)由于以文件流的形式上传时,返回的 .uploadFile() 里没法添加其它额外的参数,我们只好采取一个比较讨巧的方法:将参数拼接在 baseURL 后面。
import Moya //初始化请求的provider let MyServiceProvider = MoyaProvider<MyService>() //请求分类 public enum MyService { case upload(fileURL:URL, fileName:String) //请求数据 } //请求配置 extension MyService: TargetType { //服务器地址 public var baseURL: URL { switch self { case let .upload(_,fileName): return URL(string: "http://www.hangge.com/upload.php?fileName=" + fileName)! } } //各个请求的具体路径 public var path: String { return "" } //请求类型 public var method: Moya.Method { return .post } //请求任务事件(这里附带上参数) public var task: Task { switch self { case let .upload(fileURL, _): return .uploadFile(fileURL) } } //是否执行Alamofire验证 public var validate: Bool { return false } //这个就是做单元测试模拟的数据,只会在单元测试文件中有作用 public var sampleData: Data { return "{}".data(using: String.Encoding.utf8)! } //请求头 public var headers: [String: String]? { return nil } }
这里要特别注意:参数是拼接在 baseURL 后面,不能像下面那样拼接在 path 后面:
//服务器地址 public var baseURL: URL { return URL(string: "http://www.hangge.com")! } //各个请求的具体路径 public var path: String { switch self { case let .upload(_,fileName): return "/upload.php?fileName=" + fileName } }
因为 Moya 是通过 self = target.baseURL.appendingPathComponent(target.path) 来合并生成最终的 URL。如果我们把参数放在 path 后面,最后生成的路径中会把 ? 转义成 %3F,造成链接错误。
(2)使用时我们多传个参数:fileName(文件名)
//需要上传的文件 let fileURL = Bundle.main.url(forResource: "hangge", withExtension: "zip")! //通过Moya提交数据 MyServiceProvider.request(.upload(fileURL: fileURL, fileName: "test.zip")) { result in if case let .success(response) = result { //解析数据 let data = try? response.mapString() print(data ?? "") } }
(3)服务端收到文件后使用 $_GET["fileName"] 得到这个参数,并用其作为文件名保存。
<?php /** php 接收流文件 * @param String $file 接收后保存的文件名 * @return boolean */ function receiveStreamFile($receiveFile){ $streamData = isset($GLOBALS['HTTP_RAW_POST_DATA'])? $GLOBALS['HTTP_RAW_POST_DATA'] : ''; if(empty($streamData)){ $streamData = file_get_contents('php://input'); } if($streamData!=''){ $ret = file_put_contents($receiveFile, $streamData, true); }else{ $ret = false; } return $ret; } //定义服务器存储路径和文件名 $receiveFile = $_SERVER["DOCUMENT_ROOT"]."/file/".$_GET["fileName"] ; $ret = receiveStreamFile($receiveFile); echo json_encode(array('success'=>(bool)$ret)); ?>
全部评论(0)