通過前兩篇姿勢的入門
本文出自:http://blog.csdn.net/sk719887916/article/details/51755427 碼小白
- 如果嫌麻煩直接可以用我封裝好的庫:Novate: https://github.com/Tamicer/Novate
通過對Retrofit2.0的前兩篇的基礎(chǔ)入門和案例實(shí)踐雁歌,掌握了怎么樣使用Retrofit訪問網(wǎng)絡(luò),加入自定義header頭柱查,包括加入SSL證書廓俭,基本的調(diào)試基礎(chǔ),cookie同步問題唉工,但很多場景需求是需要文件的上傳的研乒,今天主題就來分享怎么用Retrofit2.0+ RxJava 上傳文件和圖片,表單淋硝,包括上傳Json等雹熬。其實(shí)有無rxjava,Retrofit的圖片上傳姿勢都一樣谣膳,api返回的call
換成 Observable
即可
使用 Retrofit1.x上傳文件
大家都知道在2.0以前版本上傳圖片的姿勢
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竿报,我們發(fā)現(xiàn)以前的TypedFile類型被私有化了 ,無法繼續(xù)使用1.9的傳方式继谚,因此2.x提供了上傳方案烈菌,可以MultipartBody.Part代替。
public interface FileUploadService {
@Multipart
@POST("upload")
Call<ResponseBody> upload(@Part("description") RequestBody description,
@Part MultipartBody.Part file);
}
具體用法花履。
先看一個(gè)基本的用法:
// 創(chuàng)建 RequestBody芽世,用于封裝構(gòu)建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);
// 執(zhí)行請求
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());
}
});
}
上報(bào)一張圖片
@Multipart
@POST("you methd url upload/")
Call<ResponseBody> uploadFile(
@Part() RequestBody file);
或者 :
@Multipart
@POST()
Observable<ResponseBody> uploads(
@Url String url,
@Part() MultipartBody.Part file);
上報(bào)數(shù)量確定的多張圖片
@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);
如果圖片數(shù)量不確定
@Multipart
@POST()
Observable<ResponseBody> uploadFiles(
@Url String url,
@PartMap() Map<String, RequestBody> maps);
圖文同時(shí)上報(bào)
Part方式
@Multipart
@POST("upload/")
Call<ResponseBody> register(
@FieldMap Map<String , String> usermaps,
@Part("avatar\"; filename=\"avatar.jpg") RequestBody avatar,
);
擴(kuò)展一下 將url動態(tài)化:
@Multipart
@POST
Observable<ResponseBody> uploadFileWithPartMap(
@Url() String url,
@PartMap() Map<String, RequestBody> partMap,
@Part("file") MultipartBody.Part file);
注意如果你用retrofit2.0以上的版本請看下面姿勢诡壁,否則會如下拋異常济瓢!MultipartBody.Part的參數(shù)不能指定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 );
// 執(zhí)行
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代碼:
//構(gòu)建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 );
// 執(zhí)行
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());
}
});
此種方式讓你很好的解決了用戶**注冊**問題,包含用戶全部的信息和頭像妹卿,完美解決你想用表單一起將文字和圖片一起提交的情況葬荷!
# 表單提交 #
很多時(shí)候想用表單的方式:
@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);
接著可以這樣調(diào)用:
// 執(zhí)行請求
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());
}
});
}
至于服務(wù)器返回什么類型的**model**, 開發(fā)者可以自定義, 譬如你可以把APi 中的 ResponseBody 指定為你自己的`javaBean`, 當(dāng)然上層構(gòu)建Call時(shí)候纽帖,`Callback`也必須是 `Call<MyBean>`
@POST("/uploadJson")
Call<MyBean> uploadjson(
@Body RequestBody jsonBody);
仔細(xì)的朋友會發(fā)現(xiàn)有的地方用Call宠漩,有的地方用Observable,如果結(jié)合了RxJava就是用后者接收了,這里不關(guān)乎什么方式懊直。
上面的代碼片段中顯示的代碼初始化(RequestBody 和description), 以及如何使用文件上傳API扒吁。正如剛開始已經(jīng)提到的, 從OkHttp 的RequestBody類中,需要兩個(gè)RequestBody.create()方法
除了Body描述, 必須將添加文件包裝成MultipartBody的實(shí)例室囊。這就是你需要使用適當(dāng)?shù)膹目蛻舳松蟼魑募椒?wù)端雕崩。此外, 您可以添加createFormData中的uploadFile(Uri fileUri)方法,適合相機(jī)拍照回來上傳圖片的場景融撞。
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;
}
通過以上代碼片段盼铁,可以構(gòu)造自己的file, 其他構(gòu)建ResquestBody的步驟同上所示,以上案列總有一款適合你的web后端尝偎。
設(shè)置 Content-Type
=========================
請注意設(shè)置的內(nèi)容類型饶火。如果你攔截底層OkHttp客戶端和更改內(nèi)容類型為application / json, 你的服務(wù)器可能反序列化過程出現(xiàn)的問題鹏控。請確保你沒有自定義更改multipart/form-data。
**upLoad圖片也可以具體指明Content-Type 為 “image/jpg”格式的**
RequestBody requestFile =
RequestBody.create(MediaType.parse("image/jpg"), mFile);
上傳文件到服務(wù)端示例
======================================
如果你已經(jīng)有你的后端項(xiàng)目, 您可以依靠下面的示例代碼肤寝。我們使用一個(gè)簡單api 上傳到服務(wù)器当辐。此外我們告訴api 傳入?yún)?shù)的請求, 因?yàn)槲覀兪褂玫氖荖ode.js
解析的回調(diào)函數(shù),我們記錄每個(gè)字段來顯示其輸出。
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客戶端收到返回類型的字符串, 我們將接收到的上傳成功的狀態(tài)的回調(diào)鲤看。當(dāng)然你可以處理也可以不處理狀態(tài)缘揪。下面你將看到一個(gè)成功的請求的輸出端和有效載荷的解析。第一個(gè)空對象义桂。之后,你可以看到字段只描述作為請求的一部分找筝。接著可以收到文件描述,文件大小慷吊,文件昵稱和保存路徑袖裕。
服務(wù)器解析有效數(shù)據(jù)的日志
-----------------------------
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 } ] }
---------------------------------
回顧
==
文件上傳是應(yīng)用程序中必不可卻少的功能, 你可以將此功能集成在您的應(yīng)用程序使用。本文指導(dǎo)您完成你的Android程序上報(bào)文件到您的后端服務(wù)器的第一個(gè)步驟罢浇。
文件上傳和下載進(jìn)度實(shí)現(xiàn)陆赋,請繼續(xù)關(guān)注后續(xù)文章沐祷!
**源碼:[https://github.com/Tamicer/Novate](https://github.com/Tamicer/Novate)**
Retrofit 2.0系列請閱讀簡書
更多技術(shù)文章請關(guān)注碼小白
**Retrofit 2.0+RxJava系列請閱讀**
- [Retrofit 2.0(一) 超能實(shí)踐嚷闭,完美支持Https傳輸](http://blog.csdn.net/sk719887916/article/details/51597816)
- [Retrofit2.0(二) 完美同步Cookie實(shí)現(xiàn)免登錄](http://blog.csdn.net/sk719887916/article/details/51700659)
- [Retrofit 2.0 超能實(shí)踐(三),輕松實(shí)現(xiàn)文件/圖片上傳](http://blog.csdn.net/sk719887916/article/details/51700659)
- [Retrofit 2.0 超能實(shí)踐(四)赖临,完成大文件斷點(diǎn)下載](http://www.reibang.com/p/582e0a4a4ee9)
- [基于Retrofit2.0+RxJava 封裝的超好用的RetrofitClient工具類(六)](http://blog.csdn.net/sk719887916/article/details/51958010)
- [玩轉(zhuǎn)IOC胞锰,教你徒手實(shí)現(xiàn)自定義的Retrofit框架(七)](http://blog.csdn.net/sk719887916/article/details/51957819)
- [Retrofit,Okhttp對每個(gè)Request統(tǒng)一動態(tài)添加header和參數(shù)(五)](http://blog.csdn.net/sk719887916/article/details/52132106)
- [Rxjava和Retrofit 需要掌握的幾個(gè)實(shí)用技巧,緩存問題和統(tǒng)一對有無網(wǎng)絡(luò)處理問題(八)](http://blog.csdn.net/sk719887916/article/details/52132106)
- [Novate:對Retrofit2.0的又一次完美改進(jìn)加強(qiáng)>ふァ(九)](http://blog.csdn.net/sk719887916/article/details/52195428)
第一時(shí)間獲取技術(shù)文章請關(guān)注微信公眾號嗅榕!
![開發(fā)者技術(shù)前線](http://upload-images.jianshu.io/upload_images/2022038-a7b567ef3a0b0d1f.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
>Tamic : http://www.reibang.com/users/3bbb1ddf4fd5/