FaceppClient.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. package cn.com.qmth.examcloud.web.facepp;
  2. import java.util.concurrent.TimeUnit;
  3. import org.apache.commons.codec.binary.Base64;
  4. import org.apache.commons.compress.utils.IOUtils;
  5. import org.apache.commons.lang3.StringUtils;
  6. import org.apache.http.HttpEntity;
  7. import org.apache.http.HttpStatus;
  8. import org.apache.http.client.config.CookieSpecs;
  9. import org.apache.http.client.config.RequestConfig;
  10. import org.apache.http.client.methods.CloseableHttpResponse;
  11. import org.apache.http.client.methods.HttpGet;
  12. import org.apache.http.client.methods.HttpPost;
  13. import org.apache.http.entity.mime.MultipartEntityBuilder;
  14. import org.apache.http.impl.client.CloseableHttpClient;
  15. import org.apache.http.impl.client.HttpClients;
  16. import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
  17. import org.apache.http.util.EntityUtils;
  18. import com.alibaba.fastjson.JSON;
  19. import com.alibaba.fastjson.JSONObject;
  20. import cn.com.qmth.examcloud.commons.exception.ExamCloudRuntimeException;
  21. import cn.com.qmth.examcloud.commons.exception.StatusException;
  22. import cn.com.qmth.examcloud.commons.helpers.JsonHttpResponseHolder;
  23. import cn.com.qmth.examcloud.commons.logging.ExamCloudLog;
  24. import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory;
  25. import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
  26. /**
  27. * face++ 客户端
  28. *
  29. * @author WANGWEI
  30. * @date 2019年9月16日
  31. * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
  32. */
  33. public class FaceppClient {
  34. protected static ExamCloudLog log = ExamCloudLogFactory.getLog(FaceppClient.class);
  35. private static CloseableHttpClient httpclient;
  36. private static RequestConfig requestConfig;
  37. private static FaceppClient faceppClient;
  38. private static String apiKey;
  39. private static String apiSecret;
  40. private static String compareUrl = "https://api-cn.faceplusplus.com/facepp/v3/compare";
  41. private FaceppClient() {
  42. }
  43. /**
  44. * 获取单例
  45. *
  46. * @author WANGWEI
  47. * @return
  48. */
  49. public static FaceppClient getClient() {
  50. if (null == faceppClient) {
  51. synchronized (FaceppClient.class) {
  52. if (null == faceppClient) {
  53. faceppClient = new FaceppClient();
  54. }
  55. }
  56. }
  57. return faceppClient;
  58. }
  59. static {
  60. PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(60,
  61. TimeUnit.SECONDS);
  62. cm.setValidateAfterInactivity(1000);
  63. cm.setMaxTotal(8000);
  64. cm.setDefaultMaxPerRoute(200);
  65. requestConfig = RequestConfig.custom().setConnectionRequestTimeout(500)
  66. .setSocketTimeout(10000).setConnectTimeout(10000)
  67. .setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
  68. httpclient = HttpClients.custom().setConnectionManager(cm).disableAutomaticRetries()
  69. .setDefaultRequestConfig(requestConfig).build();
  70. apiKey = PropertyHolder.getString("$facepp.apiKey");
  71. apiSecret = PropertyHolder.getString("$facepp.apiSecret");
  72. if (StringUtils.isBlank(apiKey)) {
  73. log.error("'facepp.apiKey' is not configured");
  74. }
  75. if (StringUtils.isBlank(apiSecret)) {
  76. log.error("'facepp.apiSecret' is not configured");
  77. }
  78. }
  79. /**
  80. * 人脸识别
  81. *
  82. * @author WANGWEI
  83. * @param faceToken
  84. * @param imageUrl
  85. * @return
  86. */
  87. public JsonHttpResponseHolder compareWithTokenAndImageUrl(String faceToken, String imageUrl) {
  88. if (log.isDebugEnabled()) {
  89. log.debug("[Face++]. compareWithTokenAndImageUrl(String,String); faceToken=" + faceToken
  90. + "; imageUrl=" + imageUrl);
  91. }
  92. String url = System.getProperty("facepp.compare.url");
  93. if (StringUtils.isBlank(url)) {
  94. url = compareUrl;
  95. }
  96. HttpPost httpPost = new HttpPost(url);
  97. httpPost.setConfig(FaceppClient.requestConfig);
  98. MultipartEntityBuilder builder = MultipartEntityBuilder.create();
  99. builder.addTextBody("api_key", apiKey);
  100. builder.addTextBody("api_secret", apiSecret);
  101. builder.addTextBody("face_token1", faceToken);
  102. builder.addTextBody("image_url2", imageUrl);
  103. HttpEntity httpEntity = builder.build();
  104. httpPost.setEntity(httpEntity);
  105. CloseableHttpResponse response = null;
  106. JsonHttpResponseHolder responseHolder = null;
  107. long s = System.currentTimeMillis();
  108. try {
  109. response = httpclient.execute(httpPost);
  110. int statusCode = response.getStatusLine().getStatusCode();
  111. String entityStr = EntityUtils.toString(response.getEntity(), "UTF-8");
  112. JSONObject obj = JSON.parseObject(entityStr);
  113. responseHolder = new JsonHttpResponseHolder(statusCode, obj);
  114. if (HttpStatus.SC_OK != responseHolder.getStatusCode()) {
  115. log.error("[Face++]. compareWithTokenAndImageUrl(String,String); statusCode="
  116. + statusCode + "; responseEntity=" + entityStr);
  117. } else {
  118. if (log.isDebugEnabled()) {
  119. log.debug("[Face++]. compareWithTokenAndImageUrl(String,String); statusCode="
  120. + statusCode + "; responseEntity=" + entityStr);
  121. }
  122. }
  123. } catch (Exception e) {
  124. log.error("[Face++]. compareWithTokenAndImageUrl(String,String); faceToken=" + faceToken
  125. + "; imageUrl=" + imageUrl + "; cost " + (System.currentTimeMillis() - s)
  126. + " ms.", e);
  127. throw new ExamCloudRuntimeException(e);
  128. } finally {
  129. IOUtils.closeQuietly(response);
  130. }
  131. if (log.isDebugEnabled()) {
  132. log.debug("[Face++]. compareWithTokenAndImageUrl(String,String); faceToken=" + faceToken
  133. + "; imageUrl=" + imageUrl + "; cost " + (System.currentTimeMillis() - s)
  134. + " ms.");
  135. }
  136. return responseHolder;
  137. }
  138. /**
  139. * 人脸识别<br>
  140. * 优先使用主地址调用face++<br>
  141. * 主地址无效时,使用备用地址调用face++.<br>
  142. * 备用地址也无效时,使用备用地址下载数据,使用下载数据的base64加密串调用face++<br>
  143. *
  144. * @author WANGWEI
  145. *
  146. * @param faceToken
  147. * face++预存照片
  148. * @param imageUrl
  149. * 主地址
  150. * @param backupImageUrl
  151. * 备用地址
  152. * @return
  153. * @throws StatusException
  154. * code为801,802,803表示图片地址无效
  155. */
  156. public JsonHttpResponseHolder compareWithTokenAndImageUrl(String faceToken, String imageUrl,
  157. String backupImageUrl) throws StatusException {
  158. JsonHttpResponseHolder responseHolder = null;
  159. boolean exceptionWhenUsingImageUrl = false;
  160. boolean exceptionWhenUsingBackupImageUrl = false;
  161. try {
  162. responseHolder = compareWithTokenAndImageUrl(faceToken, imageUrl);
  163. } catch (ExamCloudRuntimeException e) {
  164. exceptionWhenUsingImageUrl = true;
  165. }
  166. if (exceptionWhenUsingImageUrl) {
  167. try {
  168. responseHolder = compareWithTokenAndImageUrl(faceToken, backupImageUrl);
  169. } catch (ExamCloudRuntimeException e) {
  170. exceptionWhenUsingBackupImageUrl = true;
  171. }
  172. } else {
  173. if (HttpStatus.SC_OK == responseHolder.getStatusCode()) {
  174. return responseHolder;
  175. }
  176. String errMsg = responseHolder.getRespBody().getString("error_message");
  177. if (retry(errMsg)) {
  178. try {
  179. responseHolder = compareWithTokenAndImageUrl(faceToken, backupImageUrl);
  180. } catch (ExamCloudRuntimeException e) {
  181. exceptionWhenUsingBackupImageUrl = true;
  182. }
  183. }
  184. }
  185. if (HttpStatus.SC_OK == responseHolder.getStatusCode()) {
  186. return responseHolder;
  187. }
  188. String errMsg = responseHolder.getRespBody().getString("error_message");
  189. if (exceptionWhenUsingBackupImageUrl || retry(errMsg)) {
  190. HttpGet get = new HttpGet(backupImageUrl);
  191. get.setConfig(FaceppClient.requestConfig);
  192. CloseableHttpResponse response = null;
  193. String imageBase64 = null;
  194. long s = System.currentTimeMillis();
  195. try {
  196. response = httpclient.execute(get);
  197. if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) {
  198. throw new StatusException("801",
  199. "fail to download file. url=" + backupImageUrl);
  200. }
  201. byte[] byteArray = EntityUtils.toByteArray(response.getEntity());
  202. if (100 > byteArray.length) {
  203. throw new StatusException("802", "invalid image size. url=" + backupImageUrl);
  204. }
  205. imageBase64 = Base64.encodeBase64String(byteArray);
  206. } catch (StatusException e) {
  207. log.error("fail to download file. url=" + backupImageUrl, e);
  208. throw e;
  209. } catch (Exception e) {
  210. log.error("fail to download file. url=" + backupImageUrl, e);
  211. throw new StatusException("803", "fail to download file. url=" + backupImageUrl, e);
  212. } finally {
  213. IOUtils.closeQuietly(response);
  214. }
  215. if (log.isDebugEnabled()) {
  216. log.debug(
  217. "[Face++]. compareWithTokenAndImageUrl(String,String,String); download image; url="
  218. + backupImageUrl + "; cost " + (System.currentTimeMillis() - s)
  219. + " ms.");
  220. }
  221. responseHolder = compareWithTokenAndBase64(faceToken, imageBase64);
  222. }
  223. return responseHolder;
  224. }
  225. private boolean retry(String errMsg) {
  226. if (null != errMsg) {
  227. if (errMsg.startsWith("INVALID_IMAGE_URL")
  228. || errMsg.startsWith("IMAGE_DOWNLOAD_TIMEOUT")) {
  229. return true;
  230. }
  231. }
  232. return false;
  233. }
  234. /**
  235. * 人脸识别
  236. *
  237. * @author WANGWEI
  238. * @param faceToken
  239. * @param imageBase64
  240. * @return
  241. */
  242. public JsonHttpResponseHolder compareWithTokenAndBase64(String faceToken, String imageBase64) {
  243. String url = System.getProperty("facepp.compare.url");
  244. if (StringUtils.isBlank(url)) {
  245. url = compareUrl;
  246. }
  247. HttpPost httpPost = new HttpPost(url);
  248. httpPost.setConfig(FaceppClient.requestConfig);
  249. MultipartEntityBuilder builder = MultipartEntityBuilder.create();
  250. builder.addTextBody("api_key", apiKey);
  251. builder.addTextBody("api_secret", apiSecret);
  252. builder.addTextBody("face_token1", faceToken);
  253. builder.addTextBody("image_base64_2", imageBase64);
  254. HttpEntity httpEntity = builder.build();
  255. httpPost.setEntity(httpEntity);
  256. CloseableHttpResponse response = null;
  257. JsonHttpResponseHolder responseHolder = null;
  258. long s = System.currentTimeMillis();
  259. try {
  260. response = httpclient.execute(httpPost);
  261. int statusCode = response.getStatusLine().getStatusCode();
  262. String entityStr = EntityUtils.toString(response.getEntity(), "UTF-8");
  263. JSONObject obj = JSON.parseObject(entityStr);
  264. responseHolder = new JsonHttpResponseHolder(statusCode, obj);
  265. if (HttpStatus.SC_OK != responseHolder.getStatusCode()) {
  266. log.error("[Face++]. compareWithTokenAndBase64(String,String); statusCode="
  267. + statusCode + "; responseEntity=" + entityStr);
  268. } else {
  269. if (log.isDebugEnabled()) {
  270. log.debug("[Face++]. compareWithTokenAndBase64(String,String); statusCode="
  271. + statusCode + "; responseEntity=" + entityStr);
  272. }
  273. }
  274. } catch (Exception e) {
  275. throw new ExamCloudRuntimeException(e);
  276. } finally {
  277. IOUtils.closeQuietly(response);
  278. }
  279. if (log.isDebugEnabled()) {
  280. log.debug("[Face++]. compareWithTokenAndBase64(String,String); faceToken=" + faceToken
  281. + "; imageBase64=" + imageBase64 + "; cost " + (System.currentTimeMillis() - s)
  282. + " ms.");
  283. }
  284. return responseHolder;
  285. }
  286. /**
  287. * 人脸识别
  288. *
  289. * @author WANGWEI
  290. * @param imageUrl1
  291. * @param imageUrl2
  292. * @return
  293. */
  294. public JsonHttpResponseHolder compareWithImageUrl(String imageUrl1, String imageUrl2) {
  295. String url = System.getProperty("facepp.compare.url");
  296. if (StringUtils.isBlank(url)) {
  297. url = compareUrl;
  298. }
  299. HttpPost httpPost = new HttpPost(url);
  300. httpPost.setConfig(FaceppClient.requestConfig);
  301. MultipartEntityBuilder builder = MultipartEntityBuilder.create();
  302. builder.addTextBody("api_key", apiKey);
  303. builder.addTextBody("api_secret", apiSecret);
  304. builder.addTextBody("image_url1", imageUrl1);
  305. builder.addTextBody("image_url2", imageUrl2);
  306. HttpEntity httpEntity = builder.build();
  307. httpPost.setEntity(httpEntity);
  308. CloseableHttpResponse response = null;
  309. JsonHttpResponseHolder responseHolder = null;
  310. long s = System.currentTimeMillis();
  311. try {
  312. response = httpclient.execute(httpPost);
  313. int statusCode = response.getStatusLine().getStatusCode();
  314. String entityStr = EntityUtils.toString(response.getEntity(), "UTF-8");
  315. JSONObject obj = JSON.parseObject(entityStr);
  316. responseHolder = new JsonHttpResponseHolder(statusCode, obj);
  317. if (HttpStatus.SC_OK != responseHolder.getStatusCode()) {
  318. log.error("[Face++]. compareWithImageUrl(String,String); statusCode=" + statusCode
  319. + "; responseEntity=" + entityStr);
  320. } else {
  321. if (log.isDebugEnabled()) {
  322. log.debug("[Face++]. compareWithImageUrl(String,String); statusCode="
  323. + statusCode + "; responseEntity=" + entityStr);
  324. }
  325. }
  326. } catch (Exception e) {
  327. throw new ExamCloudRuntimeException(e);
  328. } finally {
  329. IOUtils.closeQuietly(response);
  330. }
  331. if (log.isDebugEnabled()) {
  332. log.debug("[Face++]. compareWithImageUrl(String,String); image_url1=" + imageUrl1
  333. + "; imageUrl2=" + imageUrl2 + "; cost " + (System.currentTimeMillis() - s)
  334. + " ms.");
  335. }
  336. return responseHolder;
  337. }
  338. }