Swift 上傳文件

pic.jpg

前言

最近在使用Vapor遇到很多的問(wèn)題,坑也填了不少,下面就來(lái)說(shuō)說(shuō)由這個(gè)坑引發(fā)一系列的問(wèn)題。

需求

在應(yīng)用里,需要使用保存用戶的上傳頭像,那么問(wèn)題來(lái)了,如果發(fā)布到heroku上,空間是有限的,然而用戶量是不可估計(jì)的,所以在對(duì)比了國(guó)內(nèi)幾家的OSS后,選擇了七牛對(duì)象存儲(chǔ)做為圖片的存儲(chǔ)空間,上傳的圖片的庫(kù)已經(jīng)找好,用的是Alamofire。

問(wèn)題

在使用Alamofire時(shí),發(fā)現(xiàn)一個(gè)了問(wèn)題,我們都知道Alamofire這個(gè)庫(kù)使用得最多的iOS開(kāi)發(fā),而用Alamofire做iOS的網(wǎng)絡(luò)請(qǐng)求,它的內(nèi)部返回的結(jié)果都是在主線程下執(zhí)行的,這樣做的確方便了iOS開(kāi)發(fā)的,但是在Vapor里主線程是會(huì)被攔截而不被觸發(fā)的,所以在使用Alamofire上傳圖片時(shí),結(jié)果是不會(huì)返回的。

思考

第一想到的是Alamofire是否有相關(guān)的API可以使用,但是遺憾的是,只有在返回結(jié)果后,對(duì)結(jié)果進(jìn)行處理時(shí)才有,所以這個(gè)方案fail。第三方的實(shí)現(xiàn)不了,那只能自己實(shí)現(xiàn)這個(gè)功能了。

正文

在查看了七牛的文檔后,看到七牛上傳api是表單上傳,先來(lái)看個(gè)示例。

Content-Type:   multipart/form-data; boundary=分隔線

--分隔線
Content-Disposition:       form-data; name="token"
<uploadToken>
--分隔線
Content-Disposition:       form-data; name="key"
<key>
--分隔線
Content-Disposition:       form-data; name="file"; filename="<fileName>"
Content-Type:              application/octet-stream
Content-Transfer-Encoding: binary
<fileBinaryData>
--分隔線--

上面的示例中,需要傳入的參數(shù)有三個(gè),token,key,file。在七牛中,token是要自己生成的,這里就不多說(shuō)明了,想了解的話可以私信博主。上傳的boundary=分隔線是給后臺(tái)解析時(shí)用的,博主看到Alamofire里的是以這個(gè)String(format: "Alamofire.boundary.%08x%08x", arc4random(), arc4random())為分隔線的,博主的分隔線只是把Alamofire給去掉。

最后生成像下面這樣:

Content-Type:   multipart/form-data; boundary=boundary.73e735e3732b6c0e

知道怎么生成就開(kāi)始構(gòu)建了。

let url = URL(string:"http://up.qiniu.com")

var request = URLRequest.init(url: url!);
// 請(qǐng)求類(lèi)型
request.httpMethod = "POST";
// 超時(shí)時(shí)間
request.timeoutInterval = 30;

// 設(shè)置分隔線
let boundary = String(format: "boundary.%08x%08x", arc4random(), arc4random())
let contentType = String(format: "multipart/form-data;boundary=%@", boundary)
request.addValue(contentType, forHTTPHeaderField: "Content-Type")

// 創(chuàng)建body
var body = Data();

// 請(qǐng)求參數(shù)
let dict = ["token":token,"key": key]
let keys = dict.keys;

for key in keys {
    body.append(String(format:"--%@\r\n",boundary).data(using: .utf8)!)
    body.append(String(format:"Content-Disposition:form-data;name=\"%@\"\r\n\r\n",key as String).data(using: .utf8)!)
    body.append("\(dict[key]!)\r\n".data(using: .utf8)!)
}
// 數(shù)據(jù)之前要用 --分隔線 來(lái)隔開(kāi) ,否則后臺(tái)會(huì)解析失敗
body.append(String(format:"--%@\r\n",boundary).data(using: .utf8)!)

// 文件
let key = "1.jpg"

// 文件主體
let data = UIImagePNGRepresentation(UIImage.init(named: key)!);

let file = "file"
// 傳入最后一個(gè)參數(shù)
body.append(String(format:"Content-Disposition:form-data;name=\"%@\";filename=\"\(key)\"\r\n", file).data(using: .utf8)!)

// 文件類(lèi)型
body.append("Content-Type:image/jpeg\r\n\r\n".data(using: .utf8)!)

// 添加文件主體
body.append(data)

// 使用\r\n來(lái)表示這個(gè)這個(gè)值的結(jié)束符
body.append("\r\n".data(using: .utf8)!)

// --分隔線-- 為整個(gè)表單的結(jié)束符
body.append(String(format:"--%@--\r\n",boundary).data(using: .utf8)!)

// 上傳表單
URLSession.shared.uploadTask(with: request, from: body) { (data, resp, error) in

    do{
        let d = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
        print(d)
    }catch{
        print(error)
    }
}.resume()

上面的代碼在OS上是沒(méi)問(wèn)題的,但是在Linux上就會(huì)報(bào)錯(cuò)(更新于2017.9.6)

fatal error: shared is not yet implemented: file Foundation/NSURLSession/NSURLSession.swift

這是由于使用的URLSession.shared在Linux上還沒(méi)有被實(shí)現(xiàn),這里有說(shuō)道原因。如果想了解還有那些在Linux上缺失的可以點(diǎn)這里。shared不能使用,我們就換個(gè)方法。

// 生成body的方法和上面的一樣
request.httpBody = body

// 使用URLSessionConfiguration.default來(lái)生成URLSession
let session = URLSession(
        configuration:URLSessionConfiguration.default, delegate: nil, delegateQueue: nil)

let dataTask = session.dataTask(with: request, completionHandler: {[weak self] (data, response, error) -> Void in
            let tuple = self?.c(data: data, response: response, err: error);
            completion((tuple?.0)!,tuple?.1)
        })
dataTask.resume()

以上就是一個(gè)很全面的一次表單多參數(shù)上傳的示例了,還有不明白的童鞋可以私信博主。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,578評(píng)論 19 139
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,405評(píng)論 4 61
  • 需求 當(dāng) Toolbar 布局放在底層的 Activity 中, 而Toolbar 上的菜單需要根據(jù)切換的 Fra...
    Lovemma閱讀 5,687評(píng)論 1 8
  • 蕭紅,是一位體驗(yàn)性、情緒性的才華橫溢的現(xiàn)代女作家。她的小說(shuō)具有鮮明的文體特征,清新別致的語(yǔ)言風(fēng)格、獨(dú)特的敘事...
    彎彎wwyh閱讀 7,046評(píng)論 0 2
  • 早中晚開(kāi)會(huì),強(qiáng)調(diào)基本面應(yīng)用。 晚上分總請(qǐng)全公司聚餐。酒到位后新分總開(kāi)始打感情牌。這招不錯(cuò)。
    武允兒閱讀 241評(píng)論 0 0

友情鏈接更多精彩內(nèi)容