RestApiUtils.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. package com.yiidata.intergration.api.utils;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONArray;
  4. import com.alibaba.fastjson.JSONObject;
  5. import com.datasophon.api.utils.HttpUtils;
  6. import com.google.common.io.ByteStreams;
  7. import com.google.common.net.HttpHeaders;
  8. import lombok.extern.slf4j.Slf4j;
  9. import org.apache.commons.lang.StringUtils;
  10. import org.apache.http.Header;
  11. import org.apache.http.HeaderElement;
  12. import org.apache.http.HttpEntity;
  13. import org.apache.http.HttpResponse;
  14. import org.apache.http.NameValuePair;
  15. import org.apache.http.client.HttpClient;
  16. import org.apache.http.client.config.RequestConfig;
  17. import org.apache.http.client.methods.CloseableHttpResponse;
  18. import org.apache.http.client.methods.HttpGet;
  19. import org.apache.http.client.methods.HttpPost;
  20. import org.apache.http.config.Registry;
  21. import org.apache.http.config.RegistryBuilder;
  22. import org.apache.http.config.SocketConfig;
  23. import org.apache.http.conn.socket.ConnectionSocketFactory;
  24. import org.apache.http.conn.socket.PlainConnectionSocketFactory;
  25. import org.apache.http.conn.ssl.DefaultHostnameVerifier;
  26. import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
  27. import org.apache.http.entity.StringEntity;
  28. import org.apache.http.entity.mime.MultipartEntityBuilder;
  29. import org.apache.http.impl.client.BasicCookieStore;
  30. import org.apache.http.impl.client.CloseableHttpClient;
  31. import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
  32. import org.apache.http.impl.client.HttpClients;
  33. import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
  34. import org.apache.http.params.BasicHttpParams;
  35. import org.apache.http.util.EntityUtils;
  36. import org.slf4j.Logger;
  37. import org.slf4j.LoggerFactory;
  38. import javax.net.ssl.SSLContext;
  39. import javax.net.ssl.TrustManager;
  40. import javax.net.ssl.X509TrustManager;
  41. import java.io.File;
  42. import java.io.FileNotFoundException;
  43. import java.io.FileOutputStream;
  44. import java.io.IOException;
  45. import java.io.InputStream;
  46. import java.io.OutputStream;
  47. import java.net.URLDecoder;
  48. import java.nio.charset.StandardCharsets;
  49. import java.nio.file.Paths;
  50. import java.security.KeyManagementException;
  51. import java.security.NoSuchAlgorithmException;
  52. import java.security.cert.CertificateException;
  53. import java.util.HashMap;
  54. import java.util.Map;
  55. import java.util.Objects;
  56. import java.util.Optional;
  57. /**
  58. *
  59. * 封装 RestApi 调用
  60. *
  61. * <pre>
  62. *
  63. * Created by zhaopx.
  64. * User: zhaopx
  65. * Date: 2020/4/3
  66. * Time: 17:38
  67. *
  68. * </pre>
  69. *
  70. * @author zhaopx
  71. */
  72. public class RestApiUtils {
  73. static Logger log = LoggerFactory.getLogger(RestApiUtils.class);
  74. /**
  75. * http client
  76. */
  77. static final CloseableHttpClient httpClient;
  78. /**
  79. * Cookie store
  80. */
  81. private final static BasicCookieStore cookieStore = new BasicCookieStore();
  82. static {
  83. try {
  84. //设置协议http和https对应的处理socket链接工厂的对象
  85. Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
  86. .register("http", PlainConnectionSocketFactory.INSTANCE)
  87. .register("https", new SSLConnectionSocketFactory(createIgnoreVerifySSL(), null, null, new DefaultHostnameVerifier()))
  88. .build();
  89. PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
  90. httpClient = HttpClients.custom()
  91. .setConnectionManager(cm)
  92. .setDefaultCookieStore(cookieStore)
  93. .setDefaultRequestConfig(RequestConfig.custom()
  94. .setConnectTimeout(65000)
  95. .setSocketTimeout(30000)
  96. .setConnectionRequestTimeout(30000).build())
  97. .setRetryHandler(new DefaultHttpRequestRetryHandler(3, false))
  98. .build();
  99. } catch (Exception e) {
  100. throw new IllegalStateException(e);
  101. }
  102. }
  103. /**
  104. * 绕过验证
  105. *
  106. * @return
  107. * @throws NoSuchAlgorithmException
  108. * @throws KeyManagementException
  109. */
  110. public static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
  111. SSLContext sc = SSLContext.getInstance("TLSv1.2");
  112. // 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法
  113. X509TrustManager trustManager = new X509TrustManager() {
  114. @Override
  115. public void checkClientTrusted(
  116. java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
  117. String paramString) throws CertificateException {
  118. }
  119. @Override
  120. public void checkServerTrusted(
  121. java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
  122. String paramString) throws CertificateException {
  123. }
  124. @Override
  125. public java.security.cert.X509Certificate[] getAcceptedIssuers() {
  126. return null;
  127. }
  128. };
  129. sc.init(null, new TrustManager[] { trustManager }, null);
  130. return sc;
  131. }
  132. /**
  133. * 调用一次远程 api
  134. *
  135. * @param url
  136. * @return
  137. * @throws IOException
  138. */
  139. public static Map<String, Object> post(String url) throws IOException {
  140. return post(url, new HashMap<>(), false);
  141. }
  142. /**
  143. * 调用一次远程 api
  144. *
  145. * @param url
  146. * @return
  147. * @throws IOException
  148. */
  149. public static Map<String, Object> post(String url, boolean not200ThrowError) throws IOException {
  150. return post(url, new HashMap<>(), not200ThrowError);
  151. }
  152. /**
  153. * 通过 Post 请求调用Rest 接口
  154. *
  155. * @param url
  156. * @param json
  157. * @return
  158. * @throws IOException
  159. */
  160. public static Map<String, Object> post(String url, Map<String, Object> json) throws IOException {
  161. return post(url, json, false);
  162. }
  163. /**
  164. * 通过 Post 请求调用Rest 接口
  165. *
  166. * @param url
  167. * @param json
  168. * @param not200ThrowError 为 true 时,当返回不是 200,则抛出异常
  169. * @return
  170. * @throws IOException
  171. */
  172. public static Map<String, Object> post(String url, Map<String, Object> json, boolean not200ThrowError) throws IOException {
  173. return post(url, JSON.toJSONString(json), not200ThrowError);
  174. }
  175. /**
  176. * POST 请求,执行远程
  177. *
  178. * @param url
  179. * @param jsonStr
  180. * @param not200ThrowError
  181. * @return
  182. * @throws IOException
  183. */
  184. public static Map<String, Object> post(String url, String jsonStr, boolean not200ThrowError) throws IOException {
  185. return post(url, jsonStr, new HashMap<>(), not200ThrowError);
  186. }
  187. /**
  188. * POST 请求执行远程链接
  189. *
  190. * @param url
  191. * @param jsonStr 请求 Body 体
  192. * @param header 请求头
  193. * @param not200ThrowError
  194. * @return
  195. * @throws IOException
  196. */
  197. public static Map<String, Object> post(String url, String jsonStr, Map<String, String> header, boolean not200ThrowError) throws IOException {
  198. HttpPost post = new HttpPost(url);
  199. StringEntity entity = new StringEntity(jsonStr, "UTF-8");
  200. entity.setContentType("application/json");
  201. post.setEntity(entity);
  202. if(header != null && !header.isEmpty()) {
  203. for (Map.Entry<String, String> entry : header.entrySet()) {
  204. post.addHeader(entry.getKey(), entry.getValue());
  205. }
  206. }
  207. CloseableHttpResponse resp = null;
  208. try {
  209. resp = httpClient.execute(post);
  210. log.info("execute[post] url {} return code: {}", url, resp.getStatusLine().getStatusCode());
  211. HttpEntity entity1 = resp.getEntity();
  212. String result = EntityUtils.toString(entity1);
  213. EntityUtils.consume(entity1);
  214. if(not200ThrowError && resp.getStatusLine().getStatusCode() != 200) {
  215. throw new IOException(result);
  216. }
  217. Object jsonResult = JSON.parse(result);
  218. JSONObject jsonObject = new JSONObject(2);
  219. if(jsonResult instanceof JSONArray) {
  220. jsonObject.put("result", jsonResult);
  221. } else {
  222. jsonObject = (JSONObject) jsonResult;
  223. }
  224. jsonObject.put("status_code", resp.getStatusLine().getStatusCode());
  225. return jsonObject;
  226. } finally {
  227. if(resp != null) {
  228. resp.close();
  229. }
  230. }
  231. }
  232. //---------
  233. /**
  234. * 调用一次远程 api
  235. *
  236. * @param url
  237. * @return
  238. * @throws IOException
  239. */
  240. public static Map<String, Object> get(String url) throws IOException {
  241. return get(url, new HashMap<>(), false);
  242. }
  243. /**
  244. * 调用一次远程 api
  245. *
  246. * @param url
  247. * @return
  248. * @throws IOException
  249. */
  250. public static Map<String, Object> get(String url, boolean not200ThrowError) throws IOException {
  251. return get(url, new HashMap<>(), not200ThrowError);
  252. }
  253. /**
  254. * 通过 Post 请求调用Rest 接口
  255. *
  256. * @param url
  257. * @param json
  258. * @return
  259. * @throws IOException
  260. */
  261. public static Map<String, Object> get(String url, Map<String, Object> json) throws IOException {
  262. return get(url, json, false);
  263. }
  264. /**
  265. * 通过 Post 请求调用Rest 接口
  266. *
  267. * @param url
  268. * @param json
  269. * @param not200ThrowError 为 true 时,当返回不是 200,则抛出异常
  270. * @return
  271. * @throws IOException
  272. */
  273. public static Map<String, Object> get(String url, Map<String, Object> json, boolean not200ThrowError) throws IOException {
  274. return get(url, json, new HashMap<>(), not200ThrowError);
  275. }
  276. /**
  277. * 通过 Post 请求调用Rest 接口
  278. *
  279. * @param url
  280. * @param json
  281. * @param not200ThrowError 为 true 时,当返回不是 200,则抛出异常
  282. * @return
  283. * @throws IOException
  284. */
  285. public static Map<String, Object> get(String url, Map<String, Object> json, Map<String, String> header, boolean not200ThrowError) throws IOException {
  286. HttpGet get = new HttpGet(url);
  287. if(json != null && !json.isEmpty()) {
  288. BasicHttpParams params = new BasicHttpParams();
  289. for (Map.Entry<String, Object> entry : json.entrySet()) {
  290. params.setParameter(entry.getKey(), entry.getValue());
  291. }
  292. get.setParams(params);
  293. }
  294. if(header != null && !header.isEmpty()) {
  295. for (Map.Entry<String, String> entry : header.entrySet()) {
  296. get.addHeader(entry.getKey(), entry.getValue());
  297. }
  298. }
  299. CloseableHttpResponse resp = null;
  300. try {
  301. resp = httpClient.execute(get);
  302. log.info("execute[get] url {} return code: {}", url, resp.getStatusLine().getStatusCode());
  303. final String contentType = Optional.ofNullable(resp.getFirstHeader("Content-Type")).map(Header::getValue).orElse("text/html");
  304. final HttpEntity entity = resp.getEntity();
  305. String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
  306. EntityUtils.consume(entity);
  307. if(not200ThrowError && resp.getStatusLine().getStatusCode() != 200) {
  308. throw new IOException(result);
  309. }
  310. // 判断一下返回 类型
  311. JSONObject jsonObject = new JSONObject(2);
  312. if(!contentType.contains("json")) {
  313. jsonObject.put("result", result);
  314. } else {
  315. Object jsonResult = JSON.parse(result);
  316. if(jsonResult instanceof JSONArray) {
  317. jsonObject.put("result", jsonResult);
  318. } else {
  319. jsonObject = (JSONObject) jsonResult;
  320. }
  321. }
  322. jsonObject.put("status_code", resp.getStatusLine().getStatusCode());
  323. return jsonObject;
  324. } finally {
  325. if(resp != null) {
  326. resp.close();
  327. }
  328. }
  329. }
  330. /**
  331. * 根据url下载文件,保存到filepath中
  332. *
  333. * @param url
  334. * @param downloadDir
  335. * @return 返回 下载的文件路径
  336. */
  337. public static String download(String url, File downloadDir) throws IOException {
  338. return download(url, new HashMap<>(), downloadDir, new NoProcessCall());
  339. }
  340. /**
  341. * 根据url下载文件,保存到filepath中
  342. *
  343. * @param url
  344. * @param downloadDir
  345. * @return 返回 下载的文件路径
  346. */
  347. public static String download(String url, Map<String, String> headers, File downloadDir) throws IOException {
  348. return download(url, headers, downloadDir, new NoProcessCall());
  349. }
  350. /**
  351. * 根据url下载文件,保存到filepath中
  352. *
  353. * @param url
  354. * @param downloadDir
  355. * @return 返回 下载的文件路径
  356. */
  357. public static String download(String url, Map<String, String> headers, File downloadDir, ProcessCall call) throws IOException {
  358. if(!downloadDir.exists()) {
  359. if(!downloadDir.mkdirs()) {
  360. throw new IOException(downloadDir.getAbsolutePath() + " not exists, do can not mkdir.");
  361. }
  362. }
  363. // 构造 Header,并且绑定,下载时登录,其实只要绑定 SessionID 就可以了
  364. HttpGet httpget = new HttpGet(url);
  365. if(headers != null) {
  366. for (Map.Entry<String, String> entry : headers.entrySet()) {
  367. httpget.setHeader(entry.getKey(), entry.getValue());
  368. }
  369. }
  370. // 开始下载
  371. HttpResponse response = httpClient.execute(httpget);
  372. String fileName = null;
  373. final Header contentType = response.getFirstHeader(HttpHeaders.CONTENT_TYPE);
  374. if(StringUtils.contains(contentType.getValue(), "application/octet-stream") ||
  375. StringUtils.contains(contentType.getValue(), "application/force-download")) {
  376. // 下载文件
  377. fileName = getFileName(response);
  378. if(StringUtils.isBlank(fileName)) {
  379. log.warn(response.getFirstHeader(HttpHeaders.CONTENT_DISPOSITION) + " can 'not found filename.");
  380. }
  381. }
  382. if(StringUtils.isBlank(fileName)) {
  383. fileName = getFileName(response);
  384. }
  385. if(StringUtils.isBlank(fileName)) {
  386. //无法从 header中获得文件名,如果路径是 /path/for/bar.zip 以 bar.zip 为文件名
  387. final String rawPath = httpget.getURI().getRawPath();
  388. if(!rawPath.endsWith("/")) {
  389. fileName = Paths.get(rawPath).getFileName().toString();
  390. }
  391. if(StringUtils.isBlank(fileName)) {
  392. log.warn("can not found download filename, use system timestamp.");
  393. fileName = String.valueOf(System.currentTimeMillis());
  394. }
  395. }
  396. log.info("download filename: {}", fileName);
  397. HttpEntity entity = response.getEntity();
  398. File filepath = new File(downloadDir, fileName);
  399. final long fileSize = entity.getContentLength();
  400. call.fileSize(fileSize);
  401. call.process(0.0f);
  402. try(InputStream is = entity.getContent(); FileOutputStream fileout = new FileOutputStream(filepath);) {
  403. byte[] buf = new byte[ 4 * 1024]; //4KB 缓冲区
  404. long total = 0;
  405. while (true) {
  406. int r = is.read(buf);
  407. if (r == -1) {
  408. break;
  409. }
  410. fileout.write(buf, 0, r);
  411. total += r;
  412. if(total % 2048 == 1024) {
  413. // 每 4M 推送一次进度
  414. call.process(fileSize > 0 ? (float) total / fileSize : 0.0f);
  415. }
  416. }
  417. fileout.flush();
  418. }
  419. call.process(1.0f);
  420. return filepath.getAbsolutePath();
  421. }
  422. /**
  423. * 获取response header中Content-Disposition中的filename值
  424. *
  425. * @param response
  426. * @return
  427. */
  428. private static String getFileName(HttpResponse response) {
  429. Header contentHeader = response.getFirstHeader(HttpHeaders.CONTENT_DISPOSITION);
  430. String filename = null;
  431. if (contentHeader != null) {
  432. HeaderElement[] values = contentHeader.getElements();
  433. if (values.length >= 1) {
  434. NameValuePair param = values[0].getParameterByName("filename");
  435. if (param != null) {
  436. try {
  437. if(param.getValue() != null && param.getValue().contains("%")) {
  438. //filename = new String(param.getValue().toString().getBytes(), "utf-8");
  439. filename = URLDecoder.decode(param.getValue(), "UTF-8");
  440. } else {
  441. filename = param.getValue();
  442. }
  443. } catch (Exception e) {
  444. filename = param.getValue();
  445. }
  446. }
  447. }
  448. }
  449. return filename;
  450. }
  451. /**
  452. * 根据url上传文件
  453. *
  454. * @param url
  455. * @param uploadFile
  456. * @return 返回 下载的文件路径
  457. */
  458. public static Map<String, Object> upload(String url,
  459. File uploadFile) throws IOException {
  460. return upload(url, null, null, uploadFile, true);
  461. }
  462. /**
  463. * 根据url上传文件
  464. *
  465. * @param url
  466. * @param uploadFile
  467. * @return 返回 下载的文件路径
  468. */
  469. public static Map<String, Object> upload(String url,
  470. Map<String, Object> json,
  471. File uploadFile) throws IOException {
  472. return upload(url, json, null, uploadFile, true);
  473. }
  474. /**
  475. * 根据url上传文件
  476. *
  477. * @param url
  478. * @param uploadFile
  479. * @return 返回 下载的文件路径
  480. */
  481. public static Map<String, Object> upload(String url,
  482. Map<String, Object> json,
  483. Map<String, String> headers,
  484. File uploadFile) throws IOException {
  485. return upload(url, json, headers, uploadFile, true);
  486. }
  487. /**
  488. * 根据url上传文件
  489. *
  490. * @param url
  491. * @param uploadFile
  492. * @return 返回 下载的文件路径
  493. */
  494. public static Map<String, Object> upload(String url,
  495. Map<String, Object> json,
  496. Map<String, String> headers,
  497. File uploadFile,
  498. boolean not200ThrowError) throws IOException {
  499. // 文件夹 或者 是 文件不存在
  500. if(!uploadFile.exists()) {
  501. throw new FileNotFoundException(uploadFile.getAbsolutePath() + " can not found!");
  502. }
  503. if(uploadFile.isDirectory()) {
  504. throw new IllegalStateException("upload payload must be file, but found directory.");
  505. }
  506. HttpPost httpPost = new HttpPost(url);
  507. RequestConfig requestConfig = RequestConfig.custom()
  508. .setConnectTimeout(30000) // 服务器端链接超时设置 30s
  509. .setSocketTimeout(10 * 60 * 1000) // 上传等待 10 分钟
  510. .build();
  511. httpPost.setConfig(requestConfig);
  512. if(headers != null) {
  513. for (Map.Entry<String, String> entry : headers.entrySet()) {
  514. httpPost.setHeader(entry.getKey(), entry.getValue());
  515. }
  516. }
  517. MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
  518. multipartEntityBuilder.setCharset(StandardCharsets.UTF_8);
  519. //multipartEntityBuilder.addBinaryBody("file", file,ContentType.create("image/png"),"abc.pdf");
  520. //当设置了setSocketTimeout参数后,以下代码上传PDF不能成功,将setSocketTimeout参数去掉后此可以上传成功。上传图片则没有个限制
  521. //multipartEntityBuilder.addBinaryBody("file",file,ContentType.create("application/octet-stream"),"abd.pdf");
  522. multipartEntityBuilder.addBinaryBody("file", uploadFile);
  523. if(json != null) {
  524. for (Map.Entry<String, Object> entry : json.entrySet()) {
  525. multipartEntityBuilder.addTextBody(entry.getKey(), (String) entry.getValue());
  526. }
  527. }
  528. HttpEntity httpEntity = multipartEntityBuilder.build();
  529. httpPost.setEntity(httpEntity);
  530. CloseableHttpResponse httpResponse = null;
  531. try {
  532. httpResponse = httpClient.execute(httpPost);
  533. HttpEntity responseEntity = httpResponse.getEntity();
  534. int statusCode = httpResponse.getStatusLine().getStatusCode();
  535. String result = EntityUtils.toString(responseEntity);
  536. EntityUtils.consume(responseEntity);
  537. if (not200ThrowError && httpResponse.getStatusLine().getStatusCode() != 200) {
  538. throw new IOException(result);
  539. }
  540. Object jsonResult = JSON.parse(result);
  541. JSONObject jsonObject = new JSONObject(2);
  542. if (jsonResult instanceof JSONArray) {
  543. jsonObject.put("result", jsonResult);
  544. } else {
  545. jsonObject = (JSONObject) jsonResult;
  546. }
  547. jsonObject.put("status_code", statusCode);
  548. return jsonObject;
  549. } finally {
  550. if(httpResponse != null) {
  551. httpResponse.close();
  552. }
  553. }
  554. }
  555. /**
  556. * 关闭 RPC 调用
  557. *
  558. * @throws IOException
  559. */
  560. public static void shutdown() throws IOException {
  561. httpClient.close();
  562. }
  563. /**
  564. * 下载进度回调
  565. */
  566. public static interface ProcessCall {
  567. /**
  568. * 下载的文件大小
  569. * @param size
  570. */
  571. default void fileSize(long size) {};
  572. /**
  573. * 进度推送
  574. * @param process
  575. */
  576. public void process(float process);
  577. }
  578. static class NoProcessCall implements ProcessCall {
  579. @Override
  580. public void process(float process) {
  581. }
  582. }
  583. }