Android Retrofit 實現文字(參數)和多張圖片一起上傳
需求
Retrofit普及后,最近好多人都在問,如何實現Retrofit上傳多文件+文字需求(朋友圈發圖片+文字)
解決方案
google: retrofit upload multiple files
說重點
與其直接說答案,不如我們花點時間說說多文件上傳的原理,這樣,以后就算出了其他的http框架,你也能快速實現。
HTTP協議就不講了吧?反正copy一段過來也不會有人看。我們就直接跳到文件上傳去。想看也可以,傳送門
post form 表單
上圖是不是很常見,在網頁里選一個文件,點擊上傳。上傳到哪里?服務器咯。web和移動端本質上有區別嘛?木有啊,就是一個前端展示的client。那服務器會為移動端創造一套獨立的API嘛?顯然他沒那么傻。
這個文件上傳經常會伴隨著其他fields一起上傳。可以簡單理解為表單上傳。
先來看下,如果沒有文件,也不用json,單獨上傳一些key value怎么做?在Postman里可以這樣模擬。
表單上傳要注意的是
content-type
設為application/x-www-form-urlencoded
- form表單在streaming時是
"weibo=stay4it&wechat=stay4it&qq=104816053"
多文件上傳
實際上,多文件上傳與form表單是一回事,一個key對應一個value。文件上傳就是文件名key對應文件byte[] value,如下圖postman模擬請求
只是如何標記一個key value的開始與結束呢?用&
分割肯定不夠用啦。那就得用個特殊的boudary
來做為分隔符。
另外,這個content-type
得為multiple/form-data
好了。科普到此結束。簡單理解HTTP協議以及form表單概念,相信接下來的代碼你就不只是會調用,還能明白為什么。
最原始的上傳方式
以前大家都用HttpUrlConnection,Stay在自己動手寫HTTP框架-19課時詳細講過如何上傳多文件以及進度更新。這里貼下核心代碼:
以上這個UploadUtil,拿到outputstream,分別寫入postContent
以及List<FileEntity>,代碼不多,相信大家都能看明白。
抓個包看看
請求數據抓包
上傳的有兩個form,
一個content-type
為text/plain
key為data
, value為stay4it
。
一個content-type
為image/png
key為file0
, value為文件的bytes
服務器如何接收的?(PHP版)
代碼還算好懂,$_FILES就是請求上傳的多文件,只要content-type
設置為multiple/form-data
,服務器接收是就會將其當成文件處理,將文件接收在$_FILES
中,等待處理(存數據庫,存硬盤或轉七牛云等等),$this->data是表單中key為data
所對應的valuestay4it
。(以后再有服務器er告訴你分兩個API上傳,你就可以這么懟他了: )
返回結果抓包
好了,原始的方式聊的差不多了,雖然代碼看起來很多,但已經是個util類了,倒不是那么難用。但是我們還是希望在寫代碼時能盡可能少的去關注內部實現啊。什么multiple/form-data
,什么boundary
。真是很麻煩嘛。
鳥槍換大炮吧
以下Retrofit多文件上傳內容由一葉飄舟大神提供。
Retrofit實現文件和圖片一起上傳
如果對retrofit不是很了解,參考:初識Retrofit
定義接口
根據對Stay自己動手寫HTTP框架-19課時提供的上傳圖片接口的大量抓包和測試總結,接口定義如下:
這里用到了@Partmap注解,將圖片文件信息放入map中。
準備圖片
在sdcard根目錄存放兩張圖片,分別為test.png和test.jpg(不要是gif圖片啊,服務器不支持)
代碼實現
這里就不貼代碼了,截圖如下(如果看不清,鼠標右鍵在新窗口打開就可以看到原圖了):
關鍵代碼在于:
看到這個是不是想起了上面我們提到的關鍵代碼呢?下面再貼出來我們對比下。
只要將對應的http請求頭信息填寫正確,就能上傳成功。
那么問題又來了,怎么分析和正確拼寫這個請求頭呢?
在文章開頭的時候有個抓包信息:
Content-Disposition: form-data; name="file0"; filename="test.png"
實質上上傳文件Requestbody對應的請求頭就是 name="file0"; filename="test.png",只要拼對了就沒有問題了。
注意:
- name="file0"; filename="test.png"這個請求頭是根據有心課堂提供的上傳接口寫的,不適用其他上傳接口,但原理是類似的;
- 單張圖片上傳通用的請求頭是:name="file"; filename="test.png"
- filename="test.png"這個一般是指(你希望)保存在服務器的文件名字。
舉例說明
比如我們這樣寫請求頭信息,如下代碼所示:
運行請求抓包請求頭信息如下圖所示:
出現了name="name="file1"這樣的字段,拼接錯誤(不用加name字段),服務器也毫不留情的返回了錯誤:
這個問題我當初沒有發現,后來還是請教了Stay才搞明白了。
好了,不知道我講的大家明白了沒有,最后來個成功運行的請求抓包截圖吧:
關于文字類參數上傳
寫到最后忘了說文字參數了,文字參數相對文件來說容易些。
在接口中,我們有一個文字參數 @Part("data") String des
,如果你需要多個,增加就行了。需要注意的是這個參數的名字比如"data",不是前端自定義,而是后臺定義的。
代碼托管地址:https://github.com/stay4it/RetrofitTutorial
來自:http://www.jianshu.com/p/3b8b2a0c0f30