RxFace - 用 RxJava, Retrofit, Okhttp 處理人臉識別的簡單用例
RxFace
用 RxJava, Retrofit, Okhttp 處理人臉識別的簡單用例
Overview
這是一個人臉識別的簡單 Demo, 使用了 FacePlusPlus 的接口。他們的/detection/detect
人臉識別接口可以使用普通的 get
也可以用 post
傳遞圖片二進制流的形式。其中 post
的時候遇到了相當多的坑,下面會提。
該 demo 的網絡請求庫使用了 Retrofit 并集成了 OkHttp,使用 RxJava 進行封裝,方便以流的形式處理網絡回調以及圖片處理,View 的注入框架用了 ButterKnife,圖片加載使用 Glide。
Difficult point
當直接使用 get
通過傳圖片 Url 拿到人臉識別數據的話是相當簡單的,如下請求鏈接只要使用 Retrofit
的 get
請求的 @QueryMap
傳遞參數即可: Get數據Demo。
主要存在的困難點是,當獲取本地圖片,再使用 post
傳二進制圖片數據時,post
要使用 MultipartTypedOutput
,可參考 stackoverflow 的回答。然而,這樣并沒有結束,根據 FacePlusPlus 提供的 SDK Sample 里的 Httpurlconnection 的得到的 post
請求頭是這樣的:
[Content-Disposition: form-data; name="api_key", Content-Type: text/plain; charset=US-ASCII, Content-Transfer-Encoding: 8bit]
[Content-Disposition: form-data; name="img"; filename="NoName", Content-Type: application/octet-stream, Content-Transfer-Encoding: binary]
而使用 Retrofit
默認實現的話,我們這樣來實現:
public static MultipartTypedOutput mulipartData(Bitmap bitmap, String boundary){
byte[] data = getBitmapByte(bitmap);
MultipartTypedOutput multipartTypedOutput = new MultipartTypedOutput();
multipartTypedOutput.addPart("api_key", new TypedString(Constants.API_KEY));
multipartTypedOutput.addPart("api_secret", new TypedString(Constants.API_SECRET));
multipartTypedOutput.addPart("img", new TypedByteArray("application/octet-stream", data));
return multipartTypedOutput;
}
根據 Sample 的請求頭,RestAdapter
的請求頭參數我們這樣設置來:
private RequestInterceptor mRequestInterceptor = new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {
request.addHeader("connection", "keep-alive");
request.addHeader("Content-Type", "multipart/form-data; boundary="+ getBoundary() + "; charset=UTF-8");
}
};
但是!!!它得到的String參數的頭是這樣的,這里沒有貼出其他的差異,
Content-Disposition: form-data; name="api_key"
Content-Type: text/plain; charset=UTF-8
Content-Length: 32
Content-Transfer-Encoding: 8bit
所以需要重寫三個類:
MultipartTypedOutput
為 final 類,所以重寫為 CustomMultipartTypedOutput
,并使其構造函數,增加 boundary 的設置;
TypedString
默認的編碼格式是UTF-8
,所以重寫為 AsciiTypeString
類,使其編碼格式改為 US-ASCII
;
TypedByteArray
默認的的 fileName()
方法返回的是 null,而當傳圖片數據時需要 fileName 為 "NoName",所以重寫為 CustomTypedByteArray
類,設置其 fileName 為 "NoName"。
同時需要注意的是在設置 RestAdapter
的 header 時,其 boundary 一定要和 CustomMultipartTypedOutput
的 boundary 相同,否則服務端無法匹配的!(這個地方,一時沒注意,被整了一個多小時才發現!!)
最后 body 的傳參,這樣來得到:
public static CustomMultipartTypedOutput mulipartData(Bitmap bitmap, String boundary){
byte[] data = getBitmapByte(bitmap);
CustomMultipartTypedOutput multipartTypedOutput = new CustomMultipartTypedOutput(boundary);
multipartTypedOutput.addPart("api_key", "8bit", new AsciiTypeString(Constants.API_KEY));
multipartTypedOutput.addPart("api_secret", "8bit", new AsciiTypeString(Constants.API_SECRET));
multipartTypedOutput.addPart("img", new CustomTypedByteArray("application/octet-stream", data));
return multipartTypedOutput;
}
Preview
More about me
Acknowledgments
- Glide -Glide
- Retrofit - Retrofit
- OkHttp - OkHttp
- RxJava - RxJava
- ButterKnife - ButterKnife