騰訊Ai開放服務接入
1.介面呼叫通用形式
如騰訊Ai給出的示例,其中app_id, time_stamp, nonce_str, sign為發起對騰訊Ai各種Api都需要加入到post請求的資料。而key1,key2則根據不同Api會有不同。如下面的OCR通用文字識別Api
圖中唯一不同於示例的是把key1,key2換成了image,即待識別圖片的base64編碼資料。
實際上也是如此,對待不同的Api請求,只需要修改key1,key2成對應的鍵值對即可。
2.各欄位資料生成
app_id
app_id為應用標識,到騰訊Ai平臺申請專案就會得到app_id,和app_key。app_key在後續的鑑權生成時會用到。
申請專案獲得金鑰的接入文件
time_stamp
秒級時間戳
實際上就可以使用系統時間(毫秒)/1000得到需要的值。如:
String time_stamp = System.currentTimeMillis() / 1000 + "";
nonce_str
隨機字串
public static String getRandomString(int length) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
利用隨機數字去生成即可。
image的base64編碼生成
base64的編碼工具類在網上到處都能找得到,這裡就不貼演算法了,比較長
這個部分的流程主要是將圖片以位元組資料讀入,然後再進行base64編碼
byte[] imageData = FileUtil.readFileByBytes(file);
String img64 = Base64Util.encode(imageData);
sign
先看騰訊給出的說明文件:
1.字典升序排序而且還是鍵值對,可以想到使用java中的TreeMap去處理儲存這些資料最為合適
具體簽名部分程式碼如下:
private String generateAppSign() throws UnsupportedEncodingException {
Set<String> keySet = mParams.keySet();
StringBuilder sb = new StringBuilder();
Iterator<String> iterator = keySet.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = mParams.get(key);
sb.append("&").append(key).append("=").append(URLEncoder.encode(value, "UTF-8"));
}
sb.deleteCharAt(0);
sb.append("&app_key=").append(TencentAIConstants.APP_KEY_AI);
String sign = MD5.getMD5(sb.toString());
return sign;
}
程式碼中的mParams引數就是一個TreeMap物件,儲存著除sign之外的其他鍵值對,利用這些鍵值對計算生成sign欄位。
3.發起請求
發起請求這裡使用okhttp,不使用HttpClient原因是安卓現在不支援使用HttpClient了,使用okhttp使用範圍更廣一點而且也很方便實現。主要程式碼:
MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
Iterator<String> iterator = mParams.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = mParams.get(key);
builder.addFormDataPart(key, value);
}
RequestBody requestBody = builder.build();
Request request = new Request.Builder().header("Content-Type", "application/x-www-form-urlencoded")
.url(url)
.post(requestBody)
.build();
OkHttpClient okHttpClient = new OkHttpClient();
Response response = okHttpClient.newCall(request).execute();
if (!response.isSuccessful()){
return ERROR;
}
return response.body().string();
4.使用Builder模式寫一個發起請求的類
對於這種部分引數一致,部分引數不同,且邏輯一致的需求使用Builder模式很合適。將上述列出的程式碼使用builder模式寫一個通用的類用於各類請求再合適不過了。程式碼如下:
public class AiRequestBean {
public static final String ERROR = "error";
private TreeMap<String, String> mParams;
private AiRequestBean() {
mParams = new TreeMap<>();
//時間戳
String time_stamp = System.currentTimeMillis() / 1000 + "";
//隨機字串
String nonce_str = TencentAIParamsHelper.getRandomString(10);
//appId
String app_id = String.valueOf(TencentAIConstants.APP_ID_AI);
//將通用引數設定進map中
mParams.put("app_id", app_id);
mParams.put("nonce_str", nonce_str);
mParams.put("time_stamp", time_stamp);
}
/**
* TreeMap生成鑑權資訊
*/
private String generateAppSign() throws UnsupportedEncodingException {
Set<String> keySet = mParams.keySet();
StringBuilder sb = new StringBuilder();
Iterator<String> iterator = keySet.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = mParams.get(key);
sb.append("&").append(key).append("=").append(URLEncoder.encode(value, "UTF-8"));
}
sb.deleteCharAt(0);
sb.append("&app_key=").append(TencentAIConstants.APP_KEY_AI);
String sign = MD5.getMD5(sb.toString());
return sign;
}
//發起請求
public String request(String url) throws IOException {
//生成簽名加入到引數列表中
String sign = generateAppSign();
mParams.put("sign", sign);
//使用okhttp發起請求
MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
Iterator<String> iterator = mParams.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = mParams.get(key);
builder.addFormDataPart(key, value);
}
RequestBody requestBody = builder.build();
Request request = new Request.Builder().header("Content-Type", "application/x-www-form-urlencoded")
.url(url)
.post(requestBody)
.build();
OkHttpClient okHttpClient = new OkHttpClient();
Response response = okHttpClient.newCall(request).execute();
if (!response.isSuccessful()){
return ERROR;
}
return response.body().string();
}
public static class Builder {
private AiRequestBean targetBean;
public Builder() {
targetBean = new AiRequestBean();
}
public AiRequestBean build() {
return targetBean;
}
public Builder addParam(String key, String value) {
targetBean.mParams.put(key, value);
return this;
}
}
}
public static final String URL4GENERALOCR = "https://api.ai.qq.com/fcgi-bin/ocr/ocr_generalocr";
//通用ocr文字識別請求
public String getGeneralOcrResult(File file) throws Exception {
byte[] imageData = FileUtil.readFileByBytes(file);
//準備好圖片base64資料
String img64 = Base64Util.encode(imageData);
String jsonResult = new AiRequestBean.Builder().addParam("image", img64).build().request(TencentAIConstants.URL4GENERALOCR);
return jsonResult;
}
可以看到還是很方便使用的。