日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Retrofit2 multpart多文件上传详解

發布時間:2025/4/16 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Retrofit2 multpart多文件上传详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文出處:http://www.chenkaihua.com/2016/04/02/retrofit2-upload-multipart-files.html

Retrofit2是目前很流行的android網絡框架,運用注解和動態代理,極大的簡化了網絡請求的繁瑣步驟,非常適合處理restfull網絡請求。在項目中,經常需要上傳文件到服務器,有時候是需要上傳多個文件。網上文章基本都是單文件上傳教程,這篇文章主要講retrofit的多文件上傳實現。

個人覺得有必要深入理解http協議,這樣無論使用哪個網絡框架,碰到類似這樣上傳的問題,一眼就能知道問題出在哪里。因此就有必要了解http協議的上傳機制。

一、了解 multipart/form-data

在最初的http協議中,沒有定義上傳文件的Method,為了實現這個功能,http協議組改造了post請求,添加了一種post規范,設定這種規范的Content-Type為multipart/form-data;boundary=bound,{bound}是定義的分隔符,用于分割各項內容(文件,key-value對),不然服務器無法正確識別各項內容。post body里需要用到,盡量保證隨機唯一。

post格式如下:

--${bound} Content-Disposition: form-data; name="Filename"HTTP.pdf --${bound} Content-Disposition: form-data; name="file000"; filename="HTTP協議詳解.pdf" Content-Type: application/octet-stream%PDF-1.5 file content %%EOF--${bound} Content-Disposition: form-data; name="Upload"Submit Query --${bound}--

${bound}是Content-Type里boundary的值

二、Retrofit2 對multipart/form-data的封裝

Retrofit其實是個網絡代理框架,負責封裝請求,然后把請求分發給http協議具體實現者-httpclient。retrofit默認的httpclient是okhttp。

既然Retrofit不實現http,為啥還用它呢。因為他方便!!
Retrofit會根據注解封裝網絡請求,待httpclient請求完成后,把原始response內容通過轉化器(converter)轉化成我們需要的對象(object)。

具體怎么使用 retrofit2,請參考: GitHub和Retrofit官網

那么Retrofit和okhttp怎么封裝這些multipart/form-data上傳數據呢

在retrofit中:

  • @retrofit2.http.Multipart: 標記一個請求是multipart/form-data類型,需要和@retrofit2.http.POST一同使用,并且方法參數必須是- @retrofit2.http.Part注解。
  • @retrofit2.http.Part: 代表Multipart里的一項數據,即用${bound}分隔的內容塊。

在okhttp3中:

  • okhttp3.MultipartBody: multipart/form-data的抽象封裝,繼承okhttp3.RequestBody
  • okhttp3.MultipartBody.Part: multipart/form-data里的一項數據。

三、Service接口定義

假設服務器上傳接口返回數據類型為application/json,字段如下

{ data: "上傳了3個文件", msg: "訪問成功", code: 200 }

因此需要對返回數據封裝成一個對象,考慮到復用性,封裝成泛型最佳:

public class BaseResponse<T> {public int code;public String msg;public T data; }

接著定義一個上傳的網絡請求Service:

public interface FileuploadService {/*** 通過 List<MultipartBody.Part> 傳入多個part實現多文件上傳* @param parts 每個part代表一個* @return 狀態信息*/@Multipart@POST("users/image")Call<BaseResponse<String>> uploadFilesWithParts(@Part() List<MultipartBody.Part> parts);/*** 通過 MultipartBody和@body作為參數來上傳* @param multipartBody MultipartBody包含多個Part* @return 狀態信息*/@POST("users/image")Call<BaseResponse<String>> uploadFileWithRequestBody(@Body MultipartBody multipartBody); }

由上可知,有兩種方式實現上傳

  • 使用@Multipart注解方法,并用@Part注解方法參數,類型是List< okhttp3.MultipartBody.Part>
  • 不使用@Multipart注解方法,直接使用@Body注解方法參數,類型是okhttp3.MultipartBody

可以看到,無論方法參數類型是MultipartBody.Part還是MultipartBody,這些類都不是Retrofit的類,而是okhttp實現上傳的源生類。

為什么可以這樣寫:

  • Retrofit會判斷@Body的參數類型,如果參數類型為okhttp3.RequestBody,則Retrofit不做包裝處理,直接丟給okhttp3處理。而MultipartBody是繼承RequestBody,因此Retrofit不會自動包裝這個對象。
  • 同理,Retrofit會判斷@Part的參數類型,如果參數類型為okhttp3.MultipartBody.Part,則Retrofit會把RequestBody封裝成MultipartBody,再把Part添加到MultipartBody。

四、上傳多個文件

寫好service接口后,來看看怎么構造MultipartBody
可以寫一個方法,用于把File對象轉化成MultipartBody:

public static MultipartBody filesToMultipartBody(List<File> files) {MultipartBody.Builder builder = new MultipartBody.Builder();for (File file : files) {// TODO: 16-4-2 這里為了簡單起見,沒有判斷file的類型 RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);builder.addFormDataPart("file", file.getName(), requestBody);}builder.setType(MultipartBody.FORM);MultipartBody multipartBody = builder.build();return multipartBody;}

或者把File轉化成MultipartBody.Part:

public static List<MultipartBody.Part> filesToMultipartBodyParts(List<File> files) {List<MultipartBody.Part> parts = new ArrayList<>(files.size());for (File file : files) {// TODO: 16-4-2 這里為了簡單起見,沒有判斷file的類型RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);MultipartBody.Part part = MultipartBody.Part.createFormData("file", file.getName(), requestBody);parts.add(part);}return parts;}

最后就剩下調用了

為了復用,因此把構建Retrofit簡單封裝成一個builder類:

public class RetrofitBuilder {private static Retrofit retrofit;public synchronized static Retrofit buildRetrofit() {if (retrofit == null) {HttpLoggingInterceptor logging = new HttpLoggingInterceptor();Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();GsonConverterFactory gsonConverterFactory = GsonConverterFactory.create(gson);logging.setLevel(HttpLoggingInterceptor.Level.BODY);OkHttpClient client = new OkHttpClient.Builder().addInterceptor(logging).retryOnConnectionFailure(true).build();retrofit = new Retrofit.Builder().client(client).baseUrl(Config.NetURL.BASE_URL).addConverterFactory(gsonConverterFactory).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();}return retrofit;} }

接著可以在activity里調用FileUploadService的接口了:

private void uploadFile() {MultipartBody body = MultipartBuilder.filesToMultipartBody(mFileList);RetrofitBuilder.buildRetrofit().create(FileUploadService.class).uploadFileWithRequestBody(body).enqueue(new Callback<BaseResponse<String>>() {@Overridepublic void onResponse(Call<BaseResponse<String>> call, Response<BaseResponse<String>> response) {if (response.isSuccessful()) {BaseResponse<String> body = response.body();Toast.makeText(LoginActivity.this, "上傳成功:"+response.body().getMsg(), Toast.LENGTH_SHORT).show();} else {Log.d(TAG,"上傳失敗");Toast.makeText(RegisterActivity.this, "上傳失敗", Toast.LENGTH_SHORT).show();}}@Overridepublic void onFailure(Call<BaseResponse<String>> call, Throwable t) {Toast.makeText(RegisterActivity.this, "網絡連接失敗", Toast.LENGTH_SHORT).show();}});}

參考資料:http://my.oschina.net/cnlw/blog/168466

總結

以上是生活随笔為你收集整理的Retrofit2 multpart多文件上传详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。