使用Alamofire上傳文件


UI的修改

為了便于演示,我們添加了一個(gè)upload按鈕纸镊,用于上傳之前我們下載過的文件倍阐。

image

點(diǎn)擊這個(gè)按鈕時(shí),我們將把之前下載過的文件重新上傳到api.boxue.io上逗威。為了實(shí)現(xiàn)這個(gè)功能峰搪,我們?cè)赩iewController里添加了下面的屬性:


class ViewController: UIViewController {
    // Omit for simlicity...
    var episodeUrl: NSURL?

  @IBOutlet weak var uploadBtn: UIButton!
    // Omit for simlicity...
}

其中episodeUrl用于保存下載文件的<key style="box-sizing: border-box;">NSURL</key>,uploadBtn表示關(guān)聯(lián)upload按鈕的IBOutlet凯旭。

然后概耻,在之前定義過的dest closure里使套,返回目標(biāo)路徑之前,設(shè)置這個(gè)episodeUrl:


// TODO: Add begin downloading code here
let dest: Request.DownloadFileDestination = {
  temporaryUrl, response in

    // Omit for simplicity...

    self.episodeUrl = episodeUrl
    return episodeUrl
}

并且鞠柄,在ViewController extension里侦高,我們?yōu)閏ancel按鈕設(shè)置IBAction:


extension ViewController {
  @IBAction func uploadFile(sender: AnyObject) {
        guard self.episodeUrl != nil else {
            print("Does not have any downloaded file.")
            return
        }

        // TODO: add uploading code here
        print("Uploading \(self.episodeUrl!)")
    }
}

當(dāng)然,我們忽略了upload按鈕狀態(tài)的更新厌杜,畢竟它和我們要完成的工作沒什么關(guān)系奉呛。這就是我們對(duì)上一個(gè)視頻中的App進(jìn)行的調(diào)整。接下來夯尽,我們就來實(shí)現(xiàn)上傳文件的功能瞧壮。

在Alamofire的官網(wǎng)可以看到,我們可以通過四種方式上傳文件:

image

其中前三種我們分別用一個(gè)<key style="box-sizing: border-box;">NSURL</key>匙握,NSData以及<key style="box-sizing: border-box;">NSInputStream</key>指定要上傳的內(nèi)容咆槽,而第四種MultipartFormData則是我們熟悉的模擬表單上傳。

首先圈纺,我們就從MultipartFormData開始秦忿。


模擬表單上傳文件

在uploadFile里,添加下面的代碼:


@IBAction func uploadFile(sender: AnyObject) {
    guard self.episodeUrl != nil else {
        print("Does not have any downloaded file.")
        return
    }

    print("Uploading \(self.episodeUrl!)")

    // TODO: add uploading code here
    Alamofire.upload(
        .POST,
        "https://apidemo.boxue.io/alamofire",
        multipartFormData: { multipartFormData in
            multipartFormData.appendBodyPart(
              fileURL: self.episodeUrl!, 
              name: "episode-demo")
        },
        encodingCompletion: nil
    )
}

Alamofire.upload方法的前兩個(gè)參數(shù)很好理解蛾娶,第一個(gè)參數(shù).POST是指上傳文件使用的HTTP方法灯谣,第二個(gè)參數(shù)是要上傳的地址,第三個(gè)參數(shù)是一個(gè)Closure茫叭,用于向Alamofire構(gòu)建的一個(gè)MultipartFormData對(duì)象中添加數(shù)據(jù)酬屉。在我們的例子里,我們只添加了一個(gè)name叫做"episode-demo"的字段揍愁,它的值是由self.episodeUrl指定的文件。如果我們上傳多個(gè)文件杀饵,多次調(diào)用multipartFormData.appendBodyPart就可以了莽囤。

最后一個(gè)參數(shù)encodingCompletion是一個(gè)Closure,在前面MultipartFormData編碼完成之后切距,這個(gè)Closure會(huì)被調(diào)用朽缎。稍后,我們會(huì)看到它的用法谜悟,現(xiàn)在话肖,簡單起見,我們給它傳遞nil葡幸。


在服務(wù)端準(zhǔn)備接收文件

在之前我們用到的過的apidemo.boxue.io例子里最筒,我們給AlamofireController添加下面的代碼來處理上傳的文件:


class AlamofireController extends Controller
{
  /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        if ($request->hasFile("episode-demo")) {
            $request->file('episode-demo')
              ->move(public_path().'/assets/episodes', 
                'episode-demo.mp4');
        }

        return response()->json([
                'status' => 'successful',
                'data' => 'Episodes uploaded successfully'
            ], 201);
    }
}

由于我們?cè)诳蛻舳酥兄付松蟼魑募膎ame為"episode-demo",因此蔚叨,我們先使用:


$request->hasFile("episode-demo")

判斷文件是否存在床蜘,如果文件存在我們就把它移動(dòng)到assets/episodes目錄里辙培,并且重命名為episode-demo.mp4:


if ($request->hasFile("episode-demo")) {
    $request->file('episode-demo')
      ->move(public_path().'/assets/episodes', 
        'episode-demo.mp4');
}

最后,我們向客戶端返回一個(gè)JSON表示結(jié)果:


return response()->json([
      'status' => 'successful',
      'data' => 'Episodes uploaded successfully'
  ], 201);


發(fā)送上傳請(qǐng)求

回到Xcode邢锯,Command + R編譯執(zhí)行扬蕊,我們先下載一個(gè)文件,下載完成之后丹擎,點(diǎn)擊Upload:

image

這時(shí)尾抑,無論是在控制臺(tái),還是App UI上我們都還看不到任何通知蒂培。為了確認(rèn)上傳結(jié)果蛮穿,我們只能在服務(wù)器的Web目錄里確認(rèn)文件已經(jīng)成功上傳了:

image

接下來,我們就來處理上傳進(jìn)度的問題毁渗。


使用encodingCompletion

之前践磅,我們把Alamofire.upload的encodingCompletion參數(shù)設(shè)置為了nil。實(shí)際上灸异,它是一個(gè)Closure optional府适,這個(gè)Closure接受一個(gè)MultipartFormDataEncodingResult對(duì)象做為參數(shù),并且沒有返回值肺樟。

什么是MultipartFormDataEncodingResult呢檐春?在Alamofire官網(wǎng),我們可以找到它的定義


public enum MultipartFormDataEncodingResult {
    case Success(request: Request, streamingFromDisk: Bool, streamFileURL: NSURL?)
    case Failure(ErrorType)
}

它是一個(gè)enum:

  • 成功時(shí)么伯,它的Associated value有三個(gè)值疟暖,第一個(gè)值表示發(fā)起上傳請(qǐng)求的Alamofire.Request對(duì)象;后兩個(gè)參數(shù)和編碼大文件相關(guān)田柔,我們可以暫時(shí)忽略它俐巴;
  • 失敗時(shí),它的Associated value是一個(gè)<key style="box-sizing: border-box;">NSError</key>對(duì)象硬爆,表示具體的錯(cuò)誤信息欣舵;

接下來,我們回到uploadFile缀磕,給它添加下面的代碼:


Alamofire.upload(
    .POST,
    "https://apidemo.boxue.io/alamofire",
    multipartFormData: { multipartFormData in
        multipartFormData.appendBodyPart(
          fileURL: self.episodeUrl!, 
          name: "episode-demo")
    },
    encodingCompletion: { encodingResult in
        switch encodingResult {
        case .Success(let upload, _, _):
            upload
                .progress { 
                  bytesWritten, 
                  totalBytesWritten, 
                  totalBytesExpectedToWrite in
                    print(totalBytesWritten)

                    // This closure is NOT called on the main queue for performance
                    // reasons. To update your ui, dispatch to the main queue.
                    dispatch_async(dispatch_get_main_queue()) {
                        // Calculate the download percentage
                        let progress = 
                          Float(totalBytesWritten) / 
                          Float(totalBytesExpectedToWrite)

                        self.downloadProgress.progress = progress
                    }
                }
                .responseJSON { response in
                  debugPrint(response)
              }
        case .Failure(let encodingError):
            print(encodingError)
        }
    }
)

我們著重看encodingCompletion的部分缘圈,當(dāng)編碼成功時(shí),我們讀取了它的第一個(gè)associated value袜蚕,由于它是一個(gè)Alamofire.Request對(duì)象糟把,我們可以像之前下載功能一樣,給它"注冊(cè)"進(jìn)度通知(.progress)以及結(jié)果處理(.responseJSON)牲剃,它們的用法和我們?cè)谙螺d中用到的是一樣的遣疯。

而當(dāng)編碼失敗的時(shí)候,我們只是向控制臺(tái)打印了錯(cuò)誤信息颠黎。

然后另锋,Command + R編譯執(zhí)行滞项,這次,當(dāng)我們?cè)冱c(diǎn)擊upload按鈕的時(shí)候夭坪,就可以看到進(jìn)度條更新了文判。

image

這就是Alamofire上傳文件的用法,簡單來說室梅,設(shè)置HTTP Action戏仓,設(shè)置上傳地址,編碼文件亡鼠,自定義Completion赏殃,結(jié)束。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末间涵,一起剝皮案震驚了整個(gè)濱河市仁热,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌勾哩,老刑警劉巖抗蠢,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異思劳,居然都是意外死亡迅矛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門潜叛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秽褒,“玉大人,你說我怎么就攤上這事威兜∠澹” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵牡属,是天一觀的道長票堵。 經(jīng)常有香客問我,道長逮栅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任窗宇,我火速辦了婚禮措伐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘军俊。我一直安慰自己侥加,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開白布粪躬。 她就那樣靜靜地躺著担败,像睡著了一般昔穴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上提前,一...
    開封第一講書人閱讀 51,698評(píng)論 1 305
  • 那天吗货,我揣著相機(jī)與錄音,去河邊找鬼狈网。 笑死宙搬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拓哺。 我是一名探鬼主播勇垛,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼士鸥!你這毒婦竟也來了闲孤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤烤礁,失蹤者是張志新(化名)和其女友劉穎讼积,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸽凶,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡币砂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了玻侥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片决摧。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖凑兰,靈堂內(nèi)的尸體忽然破棺而出掌桩,到底是詐尸還是另有隱情,我是刑警寧澤姑食,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布波岛,位于F島的核電站,受9級(jí)特大地震影響音半,放射性物質(zhì)發(fā)生泄漏则拷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一曹鸠、第九天 我趴在偏房一處隱蔽的房頂上張望煌茬。 院中可真熱鬧,春花似錦彻桃、人聲如沸坛善。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽眠屎。三九已至剔交,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間改衩,已是汗流浹背岖常。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留燎字,地道東北人腥椒。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像候衍,于是被迫代替她去往敵國和親笼蛛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容