需求
Retrofit普及后,最近好多人都在問,如何實(shí)現(xiàn)Retrofit上傳多文件+文字需求(朋友圈發(fā)圖片+文字)
解決方案
google: retrofit upload multiple files
說重點(diǎn)
與其直接說答案,不如我們花點(diǎn)時(shí)間說說多文件上傳的原理辈挂,這樣,以后就算出了其他的http框架裹粤,你也能快速實(shí)現(xiàn)终蒂。
HTTP協(xié)議就不講了吧?反正copy一段過來也不會(huì)有人看遥诉。我們就直接跳到文件上傳去拇泣。想看也可以,傳送門
post form 表單
上圖是不是很常見矮锈,在網(wǎng)頁里選一個(gè)文件霉翔,點(diǎn)擊上傳。上傳到哪里苞笨?服務(wù)器咯债朵。web和移動(dòng)端本質(zhì)上有區(qū)別嘛?木有啊猫缭,就是一個(gè)前端展示的client葱弟。那服務(wù)器會(huì)為移動(dòng)端創(chuàng)造一套獨(dú)立的API嘛壹店?顯然他沒那么傻猜丹。
這個(gè)文件上傳經(jīng)常會(huì)伴隨著其他fields一起上傳」杪可以簡(jiǎn)單理解為表單上傳射窒。
先來看下藏杖,如果沒有文件,也不用json脉顿,單獨(dú)上傳一些key value怎么做蝌麸?在Postman里可以這樣模擬。
表單上傳要注意的是
-
content-type
設(shè)為application/x-www-form-urlencoded
- form表單在streaming時(shí)是
"weibo=stay4it&wechat=stay4it&qq=104816053"
多文件上傳
實(shí)際上艾疟,多文件上傳與form表單是一回事来吩,一個(gè)key對(duì)應(yīng)一個(gè)value。文件上傳就是文件名key對(duì)應(yīng)文件byte[] value蔽莱,如下圖postman模擬請(qǐng)求
只是如何標(biāo)記一個(gè)key value的開始與結(jié)束呢弟疆?用&
分割肯定不夠用啦。那就得用個(gè)特殊的boudary
來做為分隔符盗冷。
另外怠苔,這個(gè)content-type
得為multiple/form-data
好了∫翘牵科普到此結(jié)束柑司。簡(jiǎn)單理解HTTP協(xié)議以及form表單概念,相信接下來的代碼你就不只是會(huì)調(diào)用锅劝,還能明白為什么攒驰。
最原始的上傳方式
以前大家都用HttpUrlConnection,Stay在自己動(dòng)手寫HTTP框架-19課時(shí)詳細(xì)講過如何上傳多文件以及進(jìn)度更新故爵。這里貼下核心代碼:
以上這個(gè)UploadUtil讼育,拿到outputstream,分別寫入postContent
以及List<FileEntity>稠集,代碼不多奶段,相信大家都能看明白。
抓個(gè)包看看
請(qǐng)求數(shù)據(jù)抓包
上傳的有兩個(gè)form剥纷,
一個(gè)content-type
為text/plain
key為data
, value為stay4it
痹籍。
一個(gè)content-type
為image/png
key為file0
, value為文件的bytes
服務(wù)器如何接收的?(PHP版)
代碼還算好懂晦鞋,$_FILES就是請(qǐng)求上傳的多文件蹲缠,只要content-type
設(shè)置為multiple/form-data
,服務(wù)器接收是就會(huì)將其當(dāng)成文件處理悠垛,將文件接收在$_FILES
中线定,等待處理(存數(shù)據(jù)庫,存硬盤或轉(zhuǎn)七牛云等等)确买,$this->data是表單中key為data
所對(duì)應(yīng)的valuestay4it
斤讥。(以后再有服務(wù)器er告訴你分兩個(gè)API上傳,你就可以這么懟他了: )
返回結(jié)果抓包
好了湾趾,原始的方式聊的差不多了芭商,雖然代碼看起來很多派草,但已經(jīng)是個(gè)util類了,倒不是那么難用铛楣。但是我們還是希望在寫代碼時(shí)能盡可能少的去關(guān)注內(nèi)部實(shí)現(xiàn)啊近迁。什么multiple/form-data
,什么boundary
簸州。真是很麻煩嘛鉴竭。
鳥槍換大炮吧
以下Retrofit多文件上傳內(nèi)容由一葉飄舟大神提供。
Retrofit實(shí)現(xiàn)文件和圖片一起上傳
如果對(duì)retrofit不是很了解岸浑,參考:初識(shí)Retrofit
定義接口
根據(jù)對(duì)Stay自己動(dòng)手寫HTTP框架-19課時(shí)提供的上傳圖片接口的大量抓包和測(cè)試總結(jié)拓瞪,接口定義如下:
這里用到了@Partmap注解,將圖片文件信息放入map中助琐。
準(zhǔn)備圖片
在sdcard根目錄存放兩張圖片祭埂,分別為test.png和test.jpg(不要是gif圖片啊,服務(wù)器不支持)
代碼實(shí)現(xiàn)
這里就不貼代碼了兵钮,截圖如下(如果看不清蛆橡,鼠標(biāo)右鍵在新窗口打開就可以看到原圖了):
關(guān)鍵代碼在于:
看到這個(gè)是不是想起了上面我們提到的關(guān)鍵代碼呢?下面再貼出來我們對(duì)比下掘譬。
只要將對(duì)應(yīng)的http請(qǐng)求頭信息填寫正確泰演,就能上傳成功。
那么問題又來了葱轩,怎么分析和正確拼寫這個(gè)請(qǐng)求頭呢睦焕?
在文章開頭的時(shí)候有個(gè)抓包信息:
Content-Disposition: form-data; name="file0"; filename="test.png"
實(shí)質(zhì)上上傳文件Requestbody對(duì)應(yīng)的請(qǐng)求頭就是 name="file0"; filename="test.png",只要拼對(duì)了就沒有問題了靴拱。
注意:
- name="file0"; filename="test.png"這個(gè)請(qǐng)求頭是根據(jù)有心課堂提供的上傳接口寫的垃喊,不適用其他上傳接口,但原理是類似的袜炕;
- 單張圖片上傳通用的請(qǐng)求頭是:name="file"; filename="test.png"
- filename="test.png"這個(gè)一般是指(你希望)保存在服務(wù)器的文件名字本谜。
舉例說明
比如我們這樣寫請(qǐng)求頭信息,如下代碼所示:
運(yùn)行請(qǐng)求抓包請(qǐng)求頭信息如下圖所示:
出現(xiàn)了name="name="file1"這樣的字段偎窘,拼接錯(cuò)誤(不用加name字段)乌助,服務(wù)器也毫不留情的返回了錯(cuò)誤:
這個(gè)問題我當(dāng)初沒有發(fā)現(xiàn),后來還是請(qǐng)教了Stay才搞明白了陌知。
好了他托,不知道我講的大家明白了沒有,最后來個(gè)成功運(yùn)行的請(qǐng)求抓包截圖吧:
關(guān)于文字類參數(shù)上傳
寫到最后忘了說文字參數(shù)了仆葡,文字參數(shù)相對(duì)文件來說容易些赏参。
在接口中,我們有一個(gè)文字參數(shù) @Part("data") String des
,如果你需要多個(gè)登刺,增加就行了。需要注意的是這個(gè)參數(shù)的名字比如"data"嗡呼,不是前端自定義纸俭,而是后臺(tái)定義的。
代碼托管地址:https://github.com/stay4it/RetrofitTutorial
2016.8.19 凌晨