Retrofit 2.0 輕鬆實現多文件/圖片上傳/Json字符串/表單

經過對Retrofit2.0的前兩篇的基礎入門和案例實踐,掌握了怎麼樣使用Retrofit訪問網絡,加入自定義header頭,包括加入SSL證書,基本的調試基礎,cookie同步問題,但不少場景需求是須要文件的上傳的,今天主題就來分享怎麼用Retrofit2.0+ RxJava 上傳文件和圖片,表單,包括上傳Json等。其實有無rxjava,Retrofit的圖片上傳姿式都同樣,api返回的call換成 Observable便可java

使用 Retrofit1.x上傳文件

你們都知道在2.0之前版本上傳圖片的姿式git

public interface ApiManager {
    @Multipart
    @POST("/user/addCarInfo")
    void addCarInfo(@QueryMap Map<String, Object> options, @Part("file") TypedFile file, Callback<JsonElement> response);

}

使用 Retrofit 2.X 上傳

Retrofit 2.X上傳文件

使用2.0,咱們發現之前的TypedFile類型被私有化了 ,沒法繼續使用1.9的傳方式,所以2.x提供了上傳方案,能夠MultipartBody.Part代替。github

public interface FileUploadService {  
 @Multipart
 @POST("upload")
 Call<ResponseBody> upload(@Part("description") RequestBody description,
                          @Part MultipartBody.Part file);
}

具體用法。web

先看一個基本的用法:json

// 建立 RequestBody,用於封裝構建RequestBody
RequestBody requestFile =
        RequestBody.create(MediaType.parse("multipart/form-data"), file);

// MultipartBody.Part  和後端約定好Key,這裏的partName是用image
MultipartBody.Part body =
        MultipartBody.Part.createFormData("image", file.getName(), requestFile);

// 添加描述
String descriptionString = "hello, 這是文件描述";
RequestBody description =
        RequestBody.create(
                MediaType.parse("multipart/form-data"), descriptionString);

// 執行請求
Call<ResponseBody> call = service.upload(description, body);
call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call,
                           Response<ResponseBody> response) {
        Log.v("Upload", "success");
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        Log.e("Upload error:", t.getMessage());
      }
    });
}

上報一張圖片

@Multipart
 @POST("you methd url upload/")
Call<ResponseBody> uploadFile(
        @Part() RequestBody file);

或者 :後端

@Multipart
@POST()
Observable<ResponseBody> uploads(
        @Url String url,
        @Part() MultipartBody.Part file);

上報數量肯定的多張圖片

@POST("upload/")
Call<ResponseBody> uploadFiles(@Part("filename") String description,
                                     @Part("pic\"; filename=\"image1.png") RequestBody imgs1,
                                     @Part("pic\"; filename=\"image2.png") RequestBody imgs2,
                                     @Part("pic\"; filename=\"image3.png") RequestBody imgs3,
                                     @Part("pic\"; filename=\"image4.png") RequestBody imgs4);

若是圖片數量不肯定

 @Multipart
 @POST()
  Observable<ResponseBody> uploadFiles(
        @Url String url,
        @PartMap() Map<String, RequestBody> maps);

圖文同時上報

Part方式api

@Multipart
@POST("upload/")
Call<ResponseBody> register(
                                   @FieldMap Map<String , String> usermaps,
                                   @Part("avatar\"; filename=\"avatar.jpg") RequestBody avatar,
                                   );

擴展一下 將url動態化:緩存

@Multipart
@POST
Observable<ResponseBody> uploadFileWithPartMap(
        @Url() String url,
        @PartMap() Map<String, RequestBody> partMap,
        @Part("file") MultipartBody.Part file);

注意若是你用retrofit2.0以上的版本請看下面姿式,不然會以下拋異常!MultipartBody.Part的參數不能指定part() 的Key。服務器

java.lang.IllegalArgumentException: @Part parameters using the MultipartBody.Part must not include a part name in the annotation. (parameter #2)
@Multipart
 @POST
 Observable<ResponseBody> uploadFileWithPartMap(
         @Url() String url,
         @PartMap() Map<String, RequestBody> partMap,
         @Part  MultipartBody.Part file);

java代碼:微信

String token ="dsdsddadad244";
    RequestBody requestFile =
                RequestBody.create(MediaType.parse("multipart/form-data"), file);

        // MultipartBody.Part is used to send also the actual file name
        MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), requestFile);

        // create a map of data to pass along
        RequestBody tokenBody = RequestBody.create(
                MediaType.parse("multipart/form-data"), token);

        HashMap<String, RequestBody> map = new HashMap<>();
        map.put("token", tokenBody);

        Call<ResponseBody> call = service.uploadFileWithPartMap(url, requestBody );
       // 執行
      call.enqueue(new Callback<ResponseBody>() {
       @Override
       public void onResponse(Call<ResponseBody> call,
        Response<ResponseBody> response) {
           Log.v("Upload", "success");
        }
    
       @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
          Log.e("Upload error:", t.getMessage());
        }
      });

更簡單的用Body方式:

 @POST()
Call<ResponseBody> upLoad(
   @Url() String url,
   @Body RequestBody Body);

若是支持RxJava就是:

@POST()
     Observable<ResponseBody> upLoad(
       @Url() String url,
       @Body RequestBody Body);
    ```` 

 Java代碼:

       //構建body
       RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
          .addFormDataPart("name", name)
          .addFormDataPart("psd", psd)
          .addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("image/*"), file))
          .build();
    
       //若是和rxjava1.x , call就換成 Observable
       Call<ResponseBody> call = service.upload(url, requestBody );
      // 執行
     call.enqueue(new Callback<ResponseBody>() {
       @Override
       public void onResponse(Call<ResponseBody> call,
        Response<ResponseBody> response) {
           Log.v("Upload", "success");
        }
    
       @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
          Log.e("Upload error:", t.getMessage());
        }
      });
  
 此種方式讓你很好的解決了用戶**註冊**問題,包含用戶所有的信息和頭像,完美解決你想用表單一塊兒將文字和圖片一塊兒提交的狀況!

# 表單提交 #

 不少時候想用表單的方式:

    @Multipart
    @POST("upload/")
    Call<ResponseBody> register(
           @Body RequestBody body
                                       );

  Java代碼:

       RequestBody requestFile =
    RequestBody.create(MediaType.parse("multipart/form-data"), file);

      Call<ResponseBody> call = service.register(body);  
    Response<ResponseBody> response = call.execute();
 

##Json提交

 
**上傳Json**

    @POST("/uploadJson")
    Observable<ResponseBody> uploadjson(
            @Body RequestBody jsonBody);

**upLoadJson 也能夠具體指明Content-Type 爲 「application/json」格式的**

具體組裝咱們的RequestBody則能夠這樣:

     RequestBody body= 
                   RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"), jsonString);

接着能夠這樣調用:

   
    // 執行請求
    Call<ResponseBody> call = service.uploadJson(description, body);
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call,
                               Response<ResponseBody> response) {
            Log.v("Upload", "success");
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.e("Upload error:", t.getMessage());
        }
    });
}

至於服務器返回什麼類型的**model**, 開發者能夠自定義, 譬如你能夠把APi 中的 ResponseBody 指定爲你本身的`javaBean`, 固然上層構建Call時候,`Callback`也必須是 `Call<MyBean>`

    @POST("/uploadJson")
    Call<MyBean> uploadjson(
            @Body RequestBody jsonBody);

仔細的朋友會發現有的地方用Call,有的地方用Observable,若是結合了RxJava就是用後者接收了,這裏不關乎什麼方式。

上面的代碼片斷中顯示的代碼初始化(RequestBody 和description), 以及如何使用文件上傳API。正如剛開始已經提到的, 從OkHttp 的RequestBody類中,須要兩個RequestBody.create()方法

除了Body描述, 必須將添加文件包裝成MultipartBody的實例。這就是你須要使用適當的從客戶端上傳文件到服務端。此外, 您能夠添加createFormData中的uploadFile(Uri fileUri)方法,適合相機拍照回來上傳圖片的場景。

      public static File getFile(Context context, Uri uri) {
        if (uri != null) {
            String path = getPath(context, uri);
            if (path != null && isLocal(path)) {
                return new File(path);
            }
        }
        return null;
    }

  經過以上代碼片斷,能夠構造本身的file, 其餘構建ResquestBody的步驟同上所示,以上案列總有一款適合你的web後端。

設置  Content-Type
=========================

請注意設置的內容類型。若是你攔截底層OkHttp客戶端和更改內容類型爲application / json, 你的服務器可能反序列化過程出現的問題。請確保你沒有自定義更改multipart/form-data。

  **upLoad圖片也能夠具體指明Content-Type 爲 「image/jpg」格式的**

       RequestBody requestFile =
                RequestBody.create(MediaType.parse("image/jpg"), mFile);


上傳文件到服務端示例
======================================

若是你已經有你的後端項目, 您能夠依靠下面的示例代碼。咱們使用一個簡單api 上傳到服務器。此外咱們告訴api 傳入參數的請求, 由於咱們使用的是Node.js  

解析的回調函數,咱們記錄每一個字段來顯示其輸出。

    method: 'POST', 
     path: '/upload',  
    config: {  
    payload: {
        maxBytes: 209715200,
        output: 'stream',
        parse: false
    },
    handler: function(request, reply) {
        var multiparty = require('multiparty');
        var form = new multiparty.Form();
        form.parse(request.payload, function(err, fields, files) {
            console.log(err);
            console.log(fields);
            console.log(files);

            return reply(util.inspect({fields: fields, files: files}));
        });
    }}

Android客戶端收到返回類型的字符串,  咱們將接收到的上傳成功的狀態的回調。固然你能夠處理也能夠不處理狀態。下面你將看到一個成功的請求的輸出端和有效載荷的解析。第一個空對象。以後,你能夠看到字段只描述做爲請求的一部分。接着能夠收到文件描述,文件大小,文件暱稱和保存路徑。

服務器解析有效數據的日誌
-----------------------------

       Null
      { description: [ 'hello, this is description speaking' ] }
    { picture:
      [ { fieldName: 'picture',
        originalFilename: '20160312_095248.jpg',
        path:      '/var/folders/rq/q_m4_21j3lqf1lw48fqttx_80000gn/T/X_sxX6LDUMBcuUcUGDMBKc2T.jpg',
       headers: [Object],
       size: 39369 } ] }
       
---------------------------------
回顧
==

文件上傳是應用程序中必不可卻少的功能, 你能夠將此功能集成在您的應用程序使用。本文指導您完成你的Android程序上報文件到您的後端服務器的第一個步驟。

文件上傳和下載進度實現,請繼續關注後續文章!

**源碼:[https://github.com/Tamicer/Novate](https://github.com/Tamicer/Novate)**

Retrofit 2.0系列請閱讀簡書 
更多技術文章請關注碼小白

**Retrofit 2.0+RxJava系列請閱讀**

- [Retrofit 2.0(一) 超能實踐,完美支持Https傳輸](http://blog.csdn.net/sk719887916/article/details/51597816)

- [Retrofit2.0(二) 完美同步Cookie實現免登陸](http://blog.csdn.net/sk719887916/article/details/51700659)
   
- [Retrofit 2.0 超能實踐(三),輕鬆實現文件/圖片上傳](http://blog.csdn.net/sk719887916/article/details/51700659)

- [Retrofit 2.0 超能實踐(四),完成大文件斷點下載](http://www.jianshu.com/p/582e0a4a4ee9)

- [基於Retrofit2.0+RxJava 封裝的超好用的RetrofitClient工具類(六)](http://blog.csdn.net/sk719887916/article/details/51958010)
 
- [玩轉IOC,教你徒手實現自定義的Retrofit框架(七)](http://blog.csdn.net/sk719887916/article/details/51957819)

- [Retrofit,Okhttp對每一個Request統一動態添加header和參數(五)](http://blog.csdn.net/sk719887916/article/details/52132106)
-  [Rxjava和Retrofit 須要掌握的幾個實用技巧,緩存問題和統一對有無網絡處理問題(八)](http://blog.csdn.net/sk719887916/article/details/52132106)
-  [Novate:對Retrofit2.0的又一次完美改進增強!(九)](http://blog.csdn.net/sk719887916/article/details/52195428)

第一時間獲取技術文章請關注微信公衆號!

![開發者技術前線](http://upload-images.jianshu.io/upload_images/2022038-a7b567ef3a0b0d1f.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
>Tamic : http://www.jianshu.com/users/3bbb1ddf4fd5/
相關文章
相關標籤/搜索