import com.alibaba.fastjson.JSON;import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.*; import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.ssl.DefaultHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.X509HostnameVerifier; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.CoreConnectionPNames; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import javax.net.ssl.*; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.SocketTimeoutException; import java.net.URLEncoder; import java.security.GeneralSecurityException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.*; @Slf4j public class HttpClientUtil { public static final String CONTENT_TYPE = "application/json"; public static final int CONNTIMEOUT = 10000; public static final int READTIMEOUT = 10000; public static final String CHARSET = "UTF-8"; private static HttpClient client = null; /**初始化httpClient**/ static { PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); cm.setMaxTotal(128); cm.setDefaultMaxPerRoute(128); client = HttpClients.custom().setConnectionManager(cm).build(); } /** * yijson方式發送一個 Post 請求, 使用指定的字符集編碼. * * @param url * @param body RequestBody * @param mimeType 例如 application/json * @param CHARSET 編碼 * @param CONNTIMEOUT 建立鏈接超時時間,毫秒. * @param READTIMEOUT 響應超時時間,毫秒. * @return ResponseBody, 使用指定的字符集編碼. * @throws ConnectTimeoutException 建立鏈接超時異常 * @throws SocketTimeoutException 響應超時 * @throws Exception */ public static String postJson(String url, String body, String mimeType, String CHARSET, Integer CONNTIMEOUT, Integer READTIMEOUT, Map<String, String> headers) throws Exception { log.info("發送HTTP(S) POST 請求:" + url + " | " + mimeType + " | " + CHARSET + " | " + CONNTIMEOUT + " | " + READTIMEOUT); HttpClient client = null; HttpResponse res = null; HttpPost post = new HttpPost(url); String result = ""; try { if (StringUtils.isNotBlank(body)) { HttpEntity entity = new StringEntity(body, ContentType.create( mimeType, CHARSET)); post.setEntity(entity); } if (headers != null && !headers.isEmpty()) { for (Map.Entry<String, String> entry : headers.entrySet()) { post.addHeader(entry.getKey(), entry.getValue()); } } // 設置參數 RequestConfig.Builder customReqConf = RequestConfig.custom(); if (CONNTIMEOUT != null) { customReqConf.setConnectTimeout(CONNTIMEOUT); } if (READTIMEOUT != null) { customReqConf.setSocketTimeout(READTIMEOUT); } post.setConfig(customReqConf.build()); if (url.startsWith("https")) { // 執行 Https 請求. client = createSSLInsecureClient(); res = client.execute(post); } else { // 執行 Http 請求. client = HttpClientUtil.client; res = client.execute(post); } result = resultPotting(res,url); } catch (Exception e) { if (res != null){ log.error("HTTP(S) POST 請求,狀態異常:{},url:{}", res.getStatusLine().getStatusCode(),url); }else{ log.error("HTTP(S) POST 請求異常 url:{}", url); } throw e; } finally { post.releaseConnection(); if (null != res) { EntityUtils.consumeQuietly(res.getEntity()); } if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) { ((CloseableHttpClient) client).close(); } } return result; } /** * 提交form表單 * * @param url * @param params * @param CONNTIMEOUT * @param READTIMEOUT * @return * @throws ConnectTimeoutException * @throws SocketTimeoutException * @throws Exception */ public static String[] postForm(String url, Map<String, String> params, Map<String, String> headers, String token, Integer CONNTIMEOUT, Integer READTIMEOUT) throws Exception { log.info("發送HTTP(S) POST 請求:" + url + " | " + CONNTIMEOUT + " | " + READTIMEOUT); HttpResponse res = null; HttpClient client = null; String[] result = new String[2]; HttpPost post = new HttpPost(url); try { if (params != null && !params.isEmpty()) { List<NameValuePair> formParams = new ArrayList<NameValuePair>(); Set<Map.Entry<String, String>> entrySet = params.entrySet(); for (Map.Entry<String, String> entry : entrySet) { formParams.add(new BasicNameValuePair(entry.getKey(), entry .getValue())); } UrlEncodedFormEntity entity = new UrlEncodedFormEntity( formParams, Consts.UTF_8); post.setEntity(entity); } if (headers != null && !headers.isEmpty()) { for (Map.Entry<String, String> entry : headers.entrySet()) { post.addHeader(entry.getKey(), entry.getValue()); } } // 設置參數 RequestConfig.Builder customReqConf = RequestConfig.custom(); if (CONNTIMEOUT != null) { customReqConf.setConnectTimeout(CONNTIMEOUT); } if (READTIMEOUT != null) { customReqConf.setSocketTimeout(READTIMEOUT); } post.setConfig(customReqConf.build()); if (url.startsWith("https")) { // 執行 Https 請求. client = createSSLInsecureClient(); res = client.execute(post); } else { // 執行 Http 請求. client = HttpClientUtil.client; res = client.execute(post); } if (res.getStatusLine().getStatusCode() == 200) { InputStream is = res.getEntity().getContent(); if (is != null) { result[0] = IOUtils.toString(is, CHARSET); } if (!StringUtils.isBlank(token)) { if (null != res.getFirstHeader(token)) { result[1] = res.getFirstHeader(token).getValue(); } } } else { // LogUtil.getAppLogger().info("HTTP(S) POSTFORM 請求,狀態異常:" + res.getStatusLine().getStatusCode() + " | " + url); throw new RuntimeException("請求通信 [ " + url + "] 時遇到異常"); } } catch (Exception e) { if (res != null) { log.error("HTTP(S) POSTFORM 請求,狀態異常:" + res.getStatusLine().getStatusCode() + " | " + url); throw e; } } finally { post.releaseConnection(); if (null != res) { EntityUtils.consumeQuietly(res.getEntity()); } if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) { ((CloseableHttpClient) client).close(); } } return result; } /** * 發送一個 GET 請求 * * @param url * @param data 必須為JSon格式 * @param CHARSET * @return * @throws Exception */ public static String get(String url, String data, String CHARSET, String token, Map<String, String> headers) throws Exception { Map<String, Object> paramsMap = new HashMap<String, Object>(); if(!StringUtils.isBlank(data)){ paramsMap = CommonUtil.convertJsonToMap(data); } String httpUrl = getHttpGetUrl(url, paramsMap, CHARSET); return get(httpUrl, CHARSET, headers, null, null, token); } /** * @description: * @param: [url 請求url, data 請求數據, CHARSET 字符編碼, token token值, headers 請求headers, response 返回值] * @return: void * @auther: wanhua.wang * @date: 2018/9/17 19:30 */ public static void getFile(String url, String data, String CHARSET, String token, Map<String, String> headers,HttpServletResponse response) throws Exception { Map<String, Object> paramsMap = new HashMap<String, Object>(); if(!StringUtils.isBlank(data)){ paramsMap = CommonUtil.convertJsonToMap(data); } String httpUrl = getHttpGetUrl(url, paramsMap, CHARSET); getWithDownload(httpUrl,headers,token,response); } /** * 獲取GET請求url * * @param url * @param params * @param encode * @return */ public static String getHttpGetUrl(String url, Map<String, Object> params, String encode) { StringBuffer buf = new StringBuffer(url); if (params != null) { // 地址增加?或者& String flag = (url.indexOf('?') == -1) ? "?" : "&"; // 添加參數 for (String name : params.keySet()) { buf.append(flag); buf.append(name); buf.append("="); try { String param = String.valueOf(params.get(name)); if (param == null) { param = ""; } buf.append(URLEncoder.encode(param, encode)); } catch (UnsupportedEncodingException e) { log.error("獲取GET請求url發生異常,參數url:{},params:{},encode:{}", url, JSON.toJSONString(params), encode, e); } flag = "&"; } } return buf.toString(); } /** * 發送一個 GET 請求 * * @param url * @param CHARSET * @param CONNTIMEOUT 建立鏈接超時時間,毫秒. * @param READTIMEOUT 響應超時時間,毫秒. * @return * @throws ConnectTimeoutException 建立鏈接超時 * @throws SocketTimeoutException 響應超時 * @throws Exception */ public static String get(String url, String CHARSET, Map<String, String> headers, Integer CONNTIMEOUT, Integer READTIMEOUT, String token) throws Exception { log.info("發送HTTP(S) GET 請求:" + url + " | " + CHARSET + " | " + CONNTIMEOUT + " | " + READTIMEOUT); HttpClient client = null; HttpResponse res = null; HttpGet get = new HttpGet(url); String result = ""; try { // 設置參數 RequestConfig.Builder customReqConf = RequestConfig.custom(); if (CONNTIMEOUT != null) { customReqConf.setConnectTimeout(CONNTIMEOUT); } if (READTIMEOUT != null) { customReqConf.setSocketTimeout(READTIMEOUT); } get.setConfig(customReqConf.build()); if (headers != null && !headers.isEmpty()) { for (Map.Entry<String, String> entry : headers.entrySet()) { get.addHeader(entry.getKey(), entry.getValue()); } } if (url.startsWith("https")) { // 執行 Https 請求. client = createSSLInsecureClient(); res = client.execute(get); } else { // 執行 Http 請求. client = HttpClientUtil.client; res = client.execute(get); } result = resultPotting(res,url); } catch (Exception e) { if (res != null) { log.error("HTTP(S) GET 請求,狀態異常:" + res.getStatusLine().getStatusCode() + " | " + url); throw e; } } finally { get.releaseConnection(); if (null != res) { EntityUtils.consumeQuietly(res.getEntity()); } if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) { ((CloseableHttpClient) client).close(); } } return result; } /** * @description:封裝接口調用返回結果 * @param: [res, token, url] * @return: java.lang.String[] * @auther: wanhua.wang * @date: 2018/8/15 17:48 */ private static String resultPotting(HttpResponse res, String url) throws IOException{ String result = ""; if (res.getStatusLine().getStatusCode() == 200) { result = IOUtils.toString(res.getEntity().getContent(), CHARSET); } else { log.info("HTTP(S) 請求,狀態異常:" + res.getStatusLine().getStatusCode() + " | " + url); throw new BizException(CodeMsgEnum.SYSTEM_INTERFACE_ERROR); } return result; } /** * 用於解決javax.net.ssl.SSLException * * @return * @throws GeneralSecurityException */ private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException { RequestConfig.Builder requestBuilder = RequestConfig.custom(); requestBuilder = requestBuilder.setSocketTimeout(10000); SSLContext sslContext = null; try { sslContext = SSLContext.getInstance("SSL"); sslContext.init(null, new TrustManager[]{new X509TrustManager() { public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } } }, new SecureRandom()); } catch (NoSuchAlgorithmException e) { log.error("createSSLInsecureClient發生異常", e); } catch (KeyManagementException e) { log.error("createSSLInsecureClient發生異常", e); } CloseableHttpClient client = HttpClientBuilder.create().setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext)).setDefaultRequestConfig(requestBuilder.build()).setSSLHostnameVerifier(new DefaultHostnameVerifier()).build(); return client; } /** * 發送下載文件的HTTP_POST請求 * * @see 1)該方法用來下載文件 * @see 2)該方法會自動關閉連接,釋放資源 * @see 3)方法內設置了連接和讀取超時(時間由本工具類全局變量限定),超時或發生其它異常將拋出RuntimeException * @see 4)請求參數含中文等特殊字符時,可直接傳入本方法,方法內部會使用本工具類設置的全局DEFAULT_CHARSET對其轉碼 * @see 5)該方法在解碼響應報文時所采用的編碼,取自響應消息頭中的[Content-Type:text/html; * charset=GBK]的charset值 * @see "若響應消息頭中未指定Content-Type屬性,則會使用HttpClient內部默認的ISO-8859-1 * @see 6)下載的文件會保存在java.io.tmpdir環境變量指定的目錄中 * @see "CentOS6.5下是/tmp,CentOS6.5下的Tomcat中是/app/tomcat/temp,Win7下是C:\Users\ * Jadyer\AppData\Local\Temp\ * @see 7)下載的文件若比較大,可能導致程序假死或內存溢出,此時可考慮在本方法內部直接輸出流 * @param reqURL * 請求地址 * @param "params * 請求參數,無參數時傳null即可 * @return 應答Map有兩個key,isSuccess--yes or * no,fullPath--isSuccess為yes時返回文件完整保存路徑,failReason-- * isSuccess為no時返回下載失敗的原因 */ public static Map<String, String> postWithDownload(String reqURL, String reqData, String contentType, Map<String, String> headers, String token) { log.info("請求{}的報文為-->>{}", reqURL,reqData); Map<String, String> resultMap = new HashMap<String, String>(); HttpClient httpClient = new DefaultHttpClient(); httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, Integer.valueOf(1000 * 60)); httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, Integer.valueOf(10000 * 60)); // 創建TrustManager(),用於解決javax.net.ssl.SSLPeerUnverifiedException: peer // not authenticated X509TrustManager trustManager = new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } }; // 創建HostnameVerifier,用於解決javax.net.ssl.SSLException: hostname in // certificate didn't match: <123.125.97.66> != <123.125.97.241> X509HostnameVerifier hostnameVerifier = new X509HostnameVerifier() { @Override public void verify(String host, SSLSocket ssl) throws IOException { } @Override public void verify(String host, X509Certificate cert) throws SSLException { } @Override public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException { } @Override public boolean verify(String arg0, SSLSession arg1) { return true; } }; HttpEntity entity = null; try { // TLS1.0是SSL3.0的升級版(網上已有人發現SSL3.0的致命BUG了),它們使用的是相同的SSLContext SSLContext sslContext = SSLContext.getInstance(org.apache.http.conn.ssl.SSLSocketFactory.TLS); // 使用TrustManager來初始化該上下文,TrustManager只是被SSL的Socket所使用 sslContext.init(null, new TrustManager[] { trustManager }, null); // 創建SSLSocketFactory org.apache.http.conn.ssl.SSLSocketFactory socketFactory = new org.apache.http.conn.ssl.SSLSocketFactory(sslContext, hostnameVerifier); // 通過SchemeRegistry將SSLSocketFactory注冊到HttpClient上 httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, socketFactory)); HttpPost httpPost = new HttpPost(reqURL); if (StringUtils.isBlank(contentType)) { httpPost.setHeader(HTTP.CONTENT_TYPE, "application/x-www-form-urlencoded; charset=" + CHARSET); } else { httpPost.setHeader(HTTP.CONTENT_TYPE, contentType); } if (headers != null && !headers.isEmpty()) { for (Map.Entry<String, String> entry : headers.entrySet()) { httpPost.addHeader(entry.getKey(), entry.getValue()); } } httpPost.setEntity(new StringEntity(null == reqData ? "" : reqData, CHARSET)); HttpResponse response = httpClient.execute(httpPost); if(response.getStatusLine().getStatusCode() == 200){ entity = response.getEntity(); resultMap = httpFileDownload(entity,response,token); log.info("請求{}得到應答<<--{}", reqURL, JSON.toJSONString(resultMap)); }else { throw new RuntimeException("請求通信[" + reqURL + "]時遇到異常"); } return resultMap; } catch (ConnectTimeoutException cte) { throw new RuntimeException("請求通信[" + reqURL + "]時連接超時", cte); } catch (SocketTimeoutException ste) { throw new RuntimeException("請求通信[" + reqURL + "]時讀取超時", ste); } catch (Exception e) { throw new RuntimeException("請求通信[" + reqURL + "]時遇到異常", e); } finally { try { EntityUtils.consume(entity); } catch (IOException e) { log.error("請求通信[" + reqURL + "]時關閉遠程應答文件流時發生異常,堆棧軌跡如下", e); } httpClient.getConnectionManager().shutdown(); } } /** * 發送下載文件的HTTP_POST請求 * * @see 1)該方法用來下載文件 * @see 2)該方法會自動關閉連接,釋放資源 * @see 3)方法內設置了連接和讀取超時(時間由本工具類全局變量限定),超時或發生其它異常將拋出RuntimeException * @see 4)請求參數含中文等特殊字符時,可直接傳入本方法,方法內部會使用本工具類設置的全局DEFAULT_CHARSET對其轉碼 * @see 5)該方法在解碼響應報文時所采用的編碼,取自響應消息頭中的[Content-Type:text/html; * charset=GBK]的charset值 * @see "若響應消息頭中未指定Content-Type屬性,則會使用HttpClient內部默認的ISO-8859-1 * @see 6)下載的文件會保存在java.io.tmpdir環境變量指定的目錄中 * @see "CentOS6.5下是/tmp,CentOS6.5下的Tomcat中是/app/tomcat/temp,Win7下是C:\Users\ * Jadyer\AppData\Local\Temp\ * @see 7)下載的文件若比較大,可能導致程序假死或內存溢出,此時可考慮在本方法內部直接輸出流 * @param url * 請求地址 * @param "params * 請求參數,無參數時傳null即可 * @return 應答Map有兩個key,isSuccess--yes or * no,fullPath--isSuccess為yes時返回文件完整保存路徑,failReason-- * isSuccess為no時返回下載失敗的原因 */ public static void getWithDownload(String url, Map<String, String> headers, String token,HttpServletResponse response) throws Exception { log.info("發送HTTP(S) GET 請求:" + url + " | " + CHARSET + " | " + CONNTIMEOUT + " | " + READTIMEOUT); HttpClient client = null; HttpResponse res = null; HttpGet get = new HttpGet(url); File tempFile = null; try { // 設置參數 RequestConfig.Builder customReqConf = RequestConfig.custom(); customReqConf.setConnectTimeout(CONNTIMEOUT); customReqConf.setSocketTimeout(READTIMEOUT); get.setConfig(customReqConf.build()); if (headers != null && !headers.isEmpty()) { for (Map.Entry<String, String> entry : headers.entrySet()) { get.addHeader(entry.getKey(), entry.getValue()); } } if (url.startsWith("https")) { // 執行 Https 請求. client = createSSLInsecureClient(); res = client.execute(get); } else { // 執行 Http 請求. client = HttpClientUtil.client; res = client.execute(get); } if(res.getStatusLine().getStatusCode() == 200){ HttpEntity entity = res.getEntity(); Map<String,String> resultMap = httpFileDownload(entity,res,token); tempFile = new File(resultMap.get("fullPath")); byte[] bytes = toByteArray(tempFile); response.getOutputStream().write(bytes,0,bytes.length); if (!StringUtils.isBlank(token)) { if (null != res.getFirstHeader(token)) { response.setHeader(token,res.getFirstHeader(token).getValue());//設置返回參數 } } log.info("請求{}得到應答<<--{}", url, JSON.toJSONString(resultMap)); }else { throw new RuntimeException("請求通信[" + url + "]時遇到異常"); } } catch (Exception e) { if (res != null) { log.error("HTTP(S) GET 請求,狀態異常:" + res.getStatusLine().getStatusCode() + " | " + url); } throw e; } finally { if (tempFile != null){ tempFile.delete(); } get.releaseConnection(); if (null != res) { EntityUtils.consumeQuietly(res.getEntity()); } if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) { ((CloseableHttpClient) client).close(); } } } /** * @description: 將文件轉換成字節流 * @param: [file 文件對象] * @return: byte[] * @auther: wanhua.wang * @date: 2018/9/17 19:46 */ public static byte[] toByteArray(File file) throws IOException { InputStream in = null; ByteArrayOutputStream out = null; try{ in = new FileInputStream(file); out = new ByteArrayOutputStream(); byte[] buffer = new byte[1024 * 4]; int n = 0; while ((n = in.read(buffer)) != -1) { out.write(buffer, 0, n); } return out.toByteArray(); }finally { if (null != in){ in.close(); } if (null != out) out.close(); } } /** * 輸入流轉字節流 * @param in * @return * @throws IOException */ public static byte[] toByteArray(InputStream in) throws IOException { ByteArrayOutputStream out = null; try{ out = new ByteArrayOutputStream(); byte[] buffer = new byte[1024 * 4]; int n = 0; while ((n = in.read(buffer)) != -1) { out.write(buffer, 0, n); } return out.toByteArray(); }finally { if (null != in){ in.close(); } if (null != out) { out.close(); } } } /** * @description:文件下載功能封裝 * @param: [entity, response, token] * @return: java.util.Map<java.lang.String,java.lang.String> * @auther: wanhua.wang * @date: 2018/9/17 19:41 */ public static Map<String,String> httpFileDownload(HttpEntity entity, HttpResponse response, String token) throws IOException { Map<String,String> resultMap = new HashMap<String, String>(); if (null != entity && (entity.getContentType().getValue() .startsWith(ContentType.APPLICATION_OCTET_STREAM.getMimeType())) || entity.getContentType().getValue().contains("image/jpeg") || entity.getContentType().getValue().contains("text/plain")) { // 文件下載成功 String filename = null; for (Header header : response.getAllHeaders()) { if (header.toString().startsWith("Content-Disposition")) { filename = header.toString().substring(header.toString().indexOf("filename=") + 10); filename = filename.substring(0, filename.length() - 1); break; } } if (StringUtils.isBlank(filename)) { Header contentHeader = response.getFirstHeader("Content-Disposition"); if (null != contentHeader) { HeaderElement[] values = contentHeader.getElements(); if (values.length == 1) { NameValuePair param = values[0].getParameterByName("filename"); if (null != param) { filename = param.getValue(); } } } } if (StringUtils.isBlank(filename)) { filename = UUID.randomUUID().toString().replaceAll("-", ""); } File _file = new File(System.getProperty("java.io.tmpdir") + "/" + filename); FileUtils.copyInputStreamToFile(entity.getContent(), _file); resultMap.put("isSuccess", "yes"); resultMap.put("fullPath", _file.getCanonicalPath()); if (!StringUtils.isBlank(token)) { if (null != response.getFirstHeader(token)) { resultMap.put(token,response.getFirstHeader(token).getValue()); } } } else { // 文件下載失敗 resultMap.put("isSuccess", "no"); resultMap.put("failReason", EntityUtils.toString(entity, CHARSET)); } return resultMap; } }
