当前位置: > > > Swift - 异步编程库PromiseKit使用详解2(URLSession的扩展)

Swift - 异步编程库PromiseKit使用详解2(URLSession的扩展)

    PromiseKit 不仅仅是一个简单的异步编程框架,它还为 Apple API 提供了许多扩展,对原有的 API 构建一个新的 Promise 实现,方便我们使用。

三、URLSession 的扩展

1,安装配置

(1)首先要安装 PromiseKit 库,具体步骤可以参考我之前的文章:

(2)接着安装 PromiseKit 的 Foundation 扩展库,从 GitHub 上下载最新的代码:

(3)将下载下来的源码包中 PMKFoundation.xcodeproj 拖拽至你的工程中

(4)工程 -> General -> Embedded Binaries 项,把 PMKFoundation.framework 添加进来。

(5)最后,在需要使用扩展的地方将其 import 进来就可以了
import PMKFoundation

2,使用 Data Task 加载数据

(1)下面样例发起一个网络请求,并将请求结果打印出来:
import UIKit
import PromiseKit
import PMKFoundation

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //创建URL对象
        let urlString = "https://httpbin.org/get?foo=bar"
        let url = URL(string:urlString)
        
        //创建请求对象
        let request = URLRequest(url: url!)
        
        //请求数据
        _ = URLSession.shared.dataTask(.promise, with: request)
            .validate() //这个也是PromiseKit提供的扩展方法,比如自动将 404 转成错误
            .done { data, response in
                let str = String(data: data, encoding: String.Encoding.utf8)
                print("--- 请求结果如下 ---")
                print(str ?? "")
            }
    }
}

(2)下面样例同样是发起一个网络请求,不过这次我们将请求结果转换成对象。
import UIKit
import PromiseKit
import PMKFoundation

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //请求数据
        fetchData(args: "foo=bar")
            .done { data in
                print("--- 请求结果 ---")
                print(data)
            }.catch { error in
                print("--- 请求失败 ---")
                print(error)
            }
    }
    
    //请求数据
    func fetchData(args: String) -> Promise<Any>{
        //创建URL对象
        let urlString = "https://httpbin.org/get?\(args)"
        let url = URL(string:urlString)
        //创建请求对象
        let request = URLRequest(url: url!)
        
        //使用PromiseKit的URLSession扩展方法获取数据
        return URLSession.shared.dataTask(.promise, with: request)
            .validate() //这个也是PromiseKit提供的扩展方法,比如自动将 404 转成错误
            .map {
                //将请求结果转成对象
                try JSONDecoder().decode(HttpBin.self, from: $0.data)
            }
    }
}

//请求结果对象
struct HttpBin: Codable {
    var origin: String
    var url: String
}

(3)当然我们也可以同时发起多个网络请求,等它们都返回后一起处理。
//请求数据
when(fulfilled: fetchData(args: "foo=bar"), fetchData(args: "name=hangge"))
    .done { data1, data2 in
        print("--- 请求结果1 ---")
        print(data1)
        print("--- 请求结果2 ---")
        print(data2)
    }.catch { error in
        print("--- 请求失败 ---")
        print(error)
    }

3,使用 Download Task 来下载文件

func sessionSimpleDownload(){
    //下载地址
    let url = URL(string: "http://hangge.com/blog/images/logo.png")
    //请求
    let request = URLRequest(url: url!)
    
    let session = URLSession.shared
    
    //文件保存路径
    let savePath = URL(fileURLWithPath: NSHomeDirectory() + "/Documents/1.png")
    
    //下载任务
    _ = session.downloadTask(.promise, with: request, to: savePath)
        .done { saveLocation, response in
            print("下载成功!保存地址如下:")
            print(saveLocation)
        }.catch { error in
            print("下载失败!错误信息如下:")
            print(error.localizedDescription)
        }
}

4,使用 Upload Task 来上传文件

func sessionUpload(){
    //上传地址
    let url = URL(string: "http://hangge.com/upload.php")
    //请求
    var request = URLRequest(url: url!, cachePolicy: .reloadIgnoringCacheData)
    request.httpMethod = "POST"
    
    let session = URLSession.shared
    
    //上传数据流
    let documents =  NSHomeDirectory() + "/Documents/1.png"
    let imgData = try! Data(contentsOf: URL(fileURLWithPath: documents))
    
    //上传任务
    _ = session.uploadTask(.promise, with: request, from: imgData)
        .done { data, response in
            print("上传成功!")
        }.catch { error in
            print("下载失败!错误信息如下:")
            print(error.localizedDescription)
    }
}

  附:服务端代码(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"]."/uploadFiles/hangge.png";  
$ret = receiveStreamFile($receiveFile);  
echo json_encode(array('success'=>(bool)$ret));  
?>
如何在上传时附带上文件名?
有时我们在文件上传的同时还会想要附带一些其它参数,比如文件名。这样服务端接收到文件后,就可以根据我们传过来的文件名来保存。实现这个其实很简单,客户端和服务端分别做如下修改。
  • 客户端:将文件名以参数的形式跟在链接后面。比如:http://hangge.com/upload.php?fileName=image1.png
  • 服务端:通过 $_GET["fileName"] 得到这个参数,并用其作为文件名保存。
评论0