当前位置: > > > Swift - 网络抽象层库Moya的使用详解5(多文件上传:MultipartFormData方式)

Swift - 网络抽象层库Moya的使用详解5(多文件上传:MultipartFormData方式)

    本文接着介绍如何通过 MultipartFormData 的形式同时上传多个文件。并且我们在上传文件的同时还可以一起提交一些其它的参数。这些额外的参数我们可以放在 body 里,也可以跟在 url 地址后面。
    下面针对这两种情况分别进行演示。

七、多文件上传(将参数放在 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
  • 1楼
    2017-10-25 17:52
    薄小白

    真不错

    站长回复

    多谢夸奖。