返回 导航

Swift

hangge.com

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)可以看到控制台不断输出已上传的进度(表示上传完毕): 

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)

回到顶部