Swift - 网络抽象层库Moya的使用详解5(多文件上传:MultipartFormData方式)
本文接着介绍如何通过 MultipartFormData 的形式同时上传多个文件。并且我们在上传文件的同时还可以一起提交一些其它的参数。这些额外的参数我们可以放在 body 里,也可以跟在 url 地址后面。
(2)可以看到控制台不断输出已上传的进度(1 则表示上传完毕):
下面针对这两种情况分别进行演示。
七、多文件上传(将参数放在 body 中)
1,网络层定义(MyServiceAPI.swift)
如果我们想将文件以及一些额外的参数都放在 request body 中提交,那么必需在 task 属性中,为每一个参数创建一个 MultipartFormData,然后返回 .uploadMultipart(_:)。
下面这个上传请求样例中,我一次上传两个文件(分别使用 Data 和 URL),同时还会附带两个其它的参数(分别是 String 和 Int 类型)
import Moya //初始化请求的provider let MyServiceProvider = MoyaProvider<MyService>() //请求分类 public enum MyService { case uploadFile(value1: String, value2: Int, file1Data:Data, file2URL: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 .uploadFile(value1, value2, file1Data, file2URL): //字符串 let strData = value1.data(using: .utf8) let formData1 = MultipartFormData(provider: .data(strData!), name: "value1") //数字 let intData = String(value2).data(using: .utf8) let formData2 = MultipartFormData(provider: .data(intData!), name: "value2") //文件1 let formData3 = MultipartFormData(provider: .data(file1Data), name: "file1", fileName: "hangge.png", mimeType: "image/png") //文件2 let formData4 = MultipartFormData(provider: .file(file2URL), name: "file2", fileName: "h.png", mimeType: "image/png") let multipartData = [formData1, formData2, formData3, formData4] return .uploadMultipart(multipartData) } } //是否执行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 file1URL = Bundle.main.url(forResource: "hangge", withExtension: "png")! let file1Data = try! Data(contentsOf: file1URL) let file2URL = Bundle.main.url(forResource: "h", withExtension: "png")! //通过Moya提交数据 MyServiceProvider.request(.uploadFile(value1: "hangge", value2: 10, file1Data: file1Data, file2URL: file2URL)) { result in if case let .success(response) = result { //解析数据 let data = try? response.mapString() print(data ?? "") } }
3,服务端代码(upload.php)
<? $value1 = $_POST["value1"]; $value2 = $_POST["value2"]; move_uploaded_file($_FILES["file1"]["tmp_name"], $_SERVER["DOCUMENT_ROOT"]."/uploadFiles/" . $_FILES["file1"]["name"]); move_uploaded_file($_FILES["file2"]["tmp_name"], $_SERVER["DOCUMENT_ROOT"]."/uploadFiles/" . $_FILES["file2"]["name"]); echo "服务器收到如下数据:\r"; echo $value1."\r"; echo $value2."\r"; echo $_FILES["file1"]["name"]."\r"; echo $_FILES["file2"]["name"]; ?>
4,运行结果
(1)发送请求后,控制台会打印出服务端返回的消息,具体如下。同时登录服务器可以看到两张图片也保存成功。
(2)通过拦截请求可以看到,所有的请求数据都是放在body中。
5,实时获取上传进度
(1)通过 progress 回调函数,我们可以实时得到当前的上传进度。
//需要上传的文件 let file1URL = Bundle.main.url(forResource: "hangge", withExtension: "png")! let file1Data = try! Data(contentsOf: file1URL) let file2URL = Bundle.main.url(forResource: "h", withExtension: "png")! //通过Moya提交数据 MyServiceProvider.request( .uploadFile(value1: "hangge", value2: 10, file1Data: file1Data, file2URL: file2URL), progress:{ progress in //实时答打印出上传进度 print("当前进度: \(progress.progress)") }) { result in if case let .success(response) = result { //解析数据 let data = try? response.mapString() print(data ?? "") } }
(2)可以看到控制台不断输出已上传的进度(1 则表示上传完毕):
八、多文件上传(将参数放在 URL 中)
1,网络层定义(MyServiceAPI.swift)
如果不想那些额外的参数也放在 request body 中提交,可以使用另一个 Task 类型:uploadCompositeMultipart(_:urlParameters)
在下面的上传请求样例中,我一次上传两个文件(分别通过 Data 和 URL)。同时还会附带两个其它的参数(分别是 String 和 Int 类型),这两个参数提交时会跟在 URL 链接后面。
(注意:高亮处表示与上面那种方式不一样的地方)
import Moya //初始化请求的provider let MyServiceProvider = MoyaProvider<MyService>() //请求分类 public enum MyService { case uploadFile(value1: String, value2: Int, file1Data:Data, file2URL: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 .uploadFile(value1, value2, file1Data, file2URL): //跟随url传递的参数 let urlParameters:[String: Any] = ["value1": value1, "value2": value2] //文件1 let formData3 = MultipartFormData(provider: .data(file1Data), name: "file1", fileName: "hangge.png", mimeType: "image/png") //文件2 let formData4 = MultipartFormData(provider: .file(file2URL), name: "file2", fileName: "h.png", mimeType: "image/png") let multipartData = [formData3, formData4] return .uploadCompositeMultipart(multipartData, urlParameters: urlParameters) } } //是否执行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,使用样例
//这个和前面是一样的,参考上面的代码
3,服务端代码(upload.php)
(注意:高亮处表示与上面那种方式不一样的地方)<? $value1 = $_GET["value1"]; $value2 = $_GET["value2"]; move_uploaded_file($_FILES["file1"]["tmp_name"], $_SERVER["DOCUMENT_ROOT"]."/uploadFiles/" . $_FILES["file1"]["name"]); move_uploaded_file($_FILES["file2"]["tmp_name"], $_SERVER["DOCUMENT_ROOT"]."/uploadFiles/" . $_FILES["file2"]["name"]); echo "服务器收到如下数据:\r"; echo $value1."\r"; echo $value2."\r"; echo $_FILES["file1"]["name"]."\r"; echo $_FILES["file2"]["name"]; ?>
4,运行结果
(1)发送请求后,控制台会打印出服务端返回的消息,具体如下。同时登录服务器可以看到两张图片也保存成功。
(2)通过拦截请求可以看到,两个文件数据是放在 body 中,而两个普通参数是跟在 url 后面。
5,实时获取上传进度
//这个在前面“将参数放在body中”这一节中已经介绍过了,具体代码可以看上面
真不错