Swift - 网络抽象层库Moya的使用详解5(多文件上传:MultipartFormData方式)
作者:hangge | 2017-10-12 08:10
本文接着介绍如何通过 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中”这一节中已经介绍过了,具体代码可以看上面
全部评论(1)
真不错
站长回复:多谢夸奖。