Android使用Retrofit上傳圖片到服務器

上傳頭像的問題

8月份的時候曾經(jīng)在項目中遇到要上傳圖片到服務器的問題,其實需求很典型:就是用戶需要上傳自己的頭像银酬。我們的項目使用的網(wǎng)絡框架是很流行的Retrofit,而網(wǎng)絡上常見的Retrofit的教程告訴我們正確的定義服務的姿勢是像這樣的:

public interface MusicListService {       
    @GET("music/listMusic.do")      
    Observable<List<Music>> getMusicList(@Query("start") int start, @Query("size") int size, @Query("categoryId") long parent_id);                                               
}

這樣形式的接口明顯不能用來上傳頭像凰荚,所以我就需要琢磨怎么實現(xiàn)圖片的上傳灵疮。實際上關于用Retrofit上傳圖片的博客在網(wǎng)上實在太多,為什么我還要單獨寫一篇拟蜻,主要是記錄一下踩過的坑绎签。而且,很多時候我們只知道怎么做酝锅,卻不知道為什么要這樣做诡必,實在不應該。只說怎么做的博客太多搔扁,我想指出來為什么這么做爸舒。

上傳實踐

以下給出一個同時傳遞字符串參數(shù)和一張圖片的服務接口的定義:

public interface UploadAvatarService {    
    @Multipart    
    @POST("user/updateAvatar.do")
    Call<Response> updateAvatar (@Query("des") String description, @Part("uploadFile\"; filename=\"test.jpg\"") RequestBody imgs );
}

然后在實例化UploadAvatarService的地方,調(diào)用以下代碼實現(xiàn)圖片上傳稿蹲。以下函數(shù)被我刪改過扭勉,可能無法一次完美運行,但大體是沒錯的苛聘。

 private void uploadFile(final String filename) {
        UploadAvatarService service = RetrofitUtil.createService(getContext(), UploadAvatarService.class);
        final File file = new File(filename);
        RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
        Call<Response> call = service.updateInfo(msg, requestBody );
        call.enqueue(new Callback<Response>() {
            @Override
            public void onResponse(Call<Response> call, retrofit2.Response<Response> response) {
                //涂炎。。设哗。唱捣。。
            }

            @Override
            public void onFailure(Call<Response> call, Throwable t) {
                //网梢。震缭。。
            }
        });
    }

POST實際提交的內(nèi)容

歷史上(1995年之前)澎粟,POST上傳數(shù)據(jù)的方式是很單一的蛀序,就像我們用GET方法傳參一樣欢瞪,參數(shù)名=參數(shù)值,參數(shù)和參數(shù)之間用&隔開徐裸。就像這樣:

param1=abc&param2=def

只不過GET的參數(shù)列表放在URL里面遣鼓,像這樣:

http://url:port?param1=abc&param2=def

而用POST傳參時,參數(shù)列表放到HTTP報文的請求體里了重贺,此時的請求頭長這樣骑祟。

POST http://www.test.org HTTP/1.1
Content-Type:application/x-www-form-urlencoded; charset=UTF-8

以前POST只支持純文本的傳輸,上傳文件就很惱火气笙,奇技淫巧應該也可以實現(xiàn)上傳文件次企,比如將二進制流當成文本傳輸。后來互聯(lián)網(wǎng)工程任務組(IETF)在1995年11月推出了RFC1867潜圃。在RFC1867中提出了基于表單的文件上傳標準缸棵。

This proposal makes two changes to HTML:

  1. Add a FILE option for the TYPE attribute of INPUT.
  2. Allow an ACCEPT attribute for INPUT tag, which is a list of
    media types or type patterns allowed for the input.

In addition, it defines a new MIME media type, multipart/form-data,
and specifies the behavior of HTML user agents when interpreting a
form with

 ENCTYPE="multipart/form-data" and/or <INPUT type="file">

tags.

簡而言之,就是要增加文件上傳的支持谭期,另外還為<input>標簽添加一個叫做accept的屬性堵第,同時定義了一個新的MIME類型,叫做multipart/form-data隧出。而multipart踏志,則是在我們傳輸非純文本的數(shù)據(jù)時采用的數(shù)據(jù)格式,比如我們上傳一個文件時胀瞪,HTTP請求頭像這樣:

POST http://www.test.org HTTP/1.1
Content-Type:multipart/form-data;  boundary=---------------------------7d52b13b519e2

如果要傳輸兩張圖片针余,請求體長這樣:

-----------------------------7d52b13b519e2
Content-Disposition: form-data; name="upload1"; filename="test.jpg"
Content-Type: image/jpeg

/**此處應是test.jpg的二進制流**/

-----------------------------7d52b13b519e2
Content-Disposition: form-data; name="upload2"; filename="test2.jpg"
Content-Type: image/jpeg

/**此處應是test2.jpg的二進制流**/

-----------------------------7d52b13b519e2--

看到請求體里面,分隔兩張圖片的二進制流和描述信息的是一段長長的橫線和一段十六進制表示的數(shù)字凄诞,這個東西稱作boundary圆雁,在RFC1867中提到了:

3.3 use of multipart/form-data

The definition of multipart/form-data is included in section 7. A
boundary is selected that does not occur in any of the data. (This
selection is sometimes done probabilisticly.)
Each field of the form
is sent, in the order in which it occurs in the form, as a part of
the multipart stream. Each part identifies the INPUT name within the
original HTML form. Each part should be labelled with an appropriate
content-type if the media type is known (e.g., inferred from the file
extension or operating system typing information) or as
application/octet-stream.

可以看到這串數(shù)字就是為了分隔不同的part的,這串數(shù)字可以是隨機生成的帆谍,但是不能出現(xiàn)在提交的表單數(shù)據(jù)里(這個很好理解摸柄,如果跟表單數(shù)據(jù)中的某一部分沖突了,就起不到分隔的作用了)既忆。

回歸那段代碼

在用Retrofit上傳文件的那段代碼中,使用@Multipart說明將使用Multipart格式提交數(shù)據(jù)嗦玖,而@Part這個注解后面跟的參數(shù)則是以下請求頭中加粗的部分:

-----------------------------7d52b13b519e2
Content-Disposition: form-data; name="upload1"; filename="test.jpg"
Content-Type: image/jpeg

這段加粗的部分放到Java代碼中需要進行轉(zhuǎn)義(對雙引號和分號轉(zhuǎn)義)患雇,就得到了如下的一個參數(shù)聲明:

@Part("uploadFile\"; filename=\"test.jpg\"") RequestBody imgs

所以@Part后面跟的參數(shù)雖然看起來很奇怪,但如果知道了實際傳輸?shù)臄?shù)據(jù)格式宇挫,這一寫法就很好理解了苛吱。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市器瘪,隨后出現(xiàn)的幾起案子翠储,更是在濱河造成了極大的恐慌绘雁,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件援所,死亡現(xiàn)場離奇詭異庐舟,居然都是意外死亡,警方通過查閱死者的電腦和手機住拭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門挪略,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人滔岳,你說我怎么就攤上這事杠娱。” “怎么了谱煤?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵摊求,是天一觀的道長。 經(jīng)常有香客問我刘离,道長室叉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任寥闪,我火速辦了婚禮太惠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘疲憋。我一直安慰自己凿渊,他們只是感情好,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布缚柳。 她就那樣靜靜地躺著埃脏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪秋忙。 梳的紋絲不亂的頭發(fā)上彩掐,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音灰追,去河邊找鬼堵幽。 笑死,一個胖子當著我的面吹牛弹澎,可吹牛的內(nèi)容都是我干的朴下。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼苦蒿,長吁一口氣:“原來是場噩夢啊……” “哼殴胧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤团滥,失蹤者是張志新(化名)和其女友劉穎竿屹,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灸姊,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡拱燃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了厨钻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扼雏。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖夯膀,靈堂內(nèi)的尸體忽然破棺而出诗充,到底是詐尸還是另有隱情,我是刑警寧澤诱建,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布蝴蜓,位于F島的核電站,受9級特大地震影響俺猿,放射性物質(zhì)發(fā)生泄漏茎匠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一押袍、第九天 我趴在偏房一處隱蔽的房頂上張望诵冒。 院中可真熱鬧,春花似錦谊惭、人聲如沸汽馋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽豹芯。三九已至,卻和暖如春驱敲,著一層夾襖步出監(jiān)牢的瞬間铁蹈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工众眨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留握牧,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓娩梨,卻偏偏與公主長得像我碟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子姚建,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

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