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)