<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>okhttp3</artifactId> <name>okhttp3</name> <version>1.0</version> <groupId>chenwc</groupId> <packaging>jar</packaging> <properties> <java.version>1.8</java.version> <slf4j.version>1.7.36</slf4j.version> <okhttp.version>4.10.0</okhttp.version> <log4j.version>1.2.17</log4j.version> <fastjson.version>1.2.83</fastjson.version> <commons-lang3.version>3.11</commons-lang3.version> <maven.compiler.plugin.version>3.6.1</maven.compiler.plugin.version> </properties> <dependencies> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>${okhttp.version}</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons-lang3.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-reload4j</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> <type>pom</type> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>${maven.compiler.plugin.version}</version> </plugin> </plugins> </build> </project>
package com.chenwc.okhttp3.utils; import okhttp3.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; /** * 网络请求工具类 * @author chenwc */ public class RestMock { private final static Logger log = LoggerFactory.getLogger(RestMock.class); private final static Long CONNECT_TIMEOUT = 30L; private final static Long CALL_TIMEOUT = 120L; private final static Long PING_INTERVAL_TIMEOUT = 5L; private final static Long READ_TIMEOUT = 60L; private final static Long WRITE_TIMEOUT = 60L; /** * 发起 post 请求 * @param url url * @param header 头信息 * @return 响应信息 */ public static String sendPost(String url, Map<String, String> header) { return sendPost(url, "", header); } /** * 通过 form 表单发起 post 请求 * @param url url * @param formBodyMap form表单 * @param header 头信息 * @return 响应信息 */ public static String sendPostByFormBody(String url, Map<String, String> formBodyMap, Map<String, String> header) { OkHttpClient client = getOkHttpClient(url.startsWith("https")); Request ok_request = getRequestBuilder(header).url(url).post(getFormBody(formBodyMap)).build(); return getResponseMessage(client, ok_request); } /** * 通过 form 表单发起 post 请求 * @param url url * @param formBodyMap form表单 * @return 响应信息 */ public static String sendPostByFormBody(String url, Map<String, String> formBodyMap) { OkHttpClient client = getOkHttpClient(url.startsWith("https")); Request ok_request = getRequestBuilder(new HashMap<>()).url(url).post(getFormBody(formBodyMap)).build(); return getResponseMessage(client, ok_request); } /** * 发起 post 请求 * @param url url * @param body 请求体 * @param header 头信息 * @return 响应信息 */ public static String sendPost(String url, String body, Map<String, String> header) { OkHttpClient client = getOkHttpClient(url.startsWith("https")); RequestBody requestBody = RequestBody.create(body.getBytes(StandardCharsets.UTF_8)); Request ok_request = getRequestBuilder(header).url(url).post(requestBody).build(); return getResponseMessage(client, ok_request); } /** * 发起 post 请求,提交表单文件 * 文件表单格式:form-data; name="file"; filename="xxxxxxxxxx" * @param url url * @param file 文件 * @param header 头信息 * @return 响应信息 */ public static String sendPostFileAndFormBody(String url, File file, Map<String, String> header) { return sendPostFileAndFormBody(url, file, new HashMap<>(), header); } /** * 发起 post 请求,提交文件和表单 * 文件表单格式:form-data; name="file"; filename="xxxxxxxxxx" * 表单格式:form-data; name="key1" * 表单格式:form-data; name="key2" * @param url url * @param file 文件 * @param formBodyMap 表单参数 * @param header 头信息 * @return 响应信息 */ public static String sendPostFileAndFormBody(String url, File file, Map<String, String> formBodyMap, Map<String, String> header) { OkHttpClient client = getOkHttpClient(url.startsWith("https")); //声明请求体的类型为文件表单类型 MediaType mediaType = MediaType.parse("multipart/form-data"); //通过静态方法创建请求体 //file为要上传的文件,mediaType为上一步中 声明的请求体类型 RequestBody requestBody = RequestBody.create(file, mediaType); //创建文件表单的请求体,把文件请求体、表单参数放入表单中 MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder(); //添加文件请求体 multipartBodyBuilder.addFormDataPart("file", file.getName(), requestBody); //添加表单参数 if (null != formBodyMap && !formBodyMap.isEmpty()){ for (String key : formBodyMap.keySet()) { multipartBodyBuilder.addFormDataPart(key, formBodyMap.get(key)); } } Request ok_request = getRequestBuilder(header).url(url).post(multipartBodyBuilder.build()).build(); return getResponseMessage(client, ok_request); } /** * 发起 get 请求 * @param url url * @return 响应信息 */ public static String sendGet(String url) { return sendGet(url, new HashMap<>(), new HashMap<>()); } /** * 发起 get 请求 * @param url url * @param header 头信息 * @return 响应信息 */ public static String sendGet(String url, Map<String, String> header) { return sendGet(url, new HashMap<>(), header); } /** * 发起 get 请求 * @param url url * @param parameter 请求参数 * @param header 头信息 * @return 响应信息 */ public static String sendGet(String url, Map<String, String> parameter, Map<String, String> header) { OkHttpClient client = getOkHttpClient(url.startsWith("https")); Request ok_request = getRequestBuilder(header).url(generateDoGetRequestParameter(url, parameter).build()).get().build(); return getResponseMessage(client, ok_request); } /** * 根据头信息 map 获取 Request.Builder * @param header 头信息 * @return Request.Builder */ static Request.Builder getRequestBuilder(Map<String, String> header){ Request.Builder requestBuilder = new Request.Builder(); //装入头信息 if (null != header && !header.isEmpty()) { for (String key : header.keySet()) { requestBuilder.addHeader(key, header.get(key)); } } return requestBuilder; } /** * 根据 map 生成 FormBody * @param formBodyMap form表单数据 * @return RequestBody */ private static RequestBody getFormBody(Map<String, String> formBodyMap){ //form表单 FormBody.Builder formBuilder = new FormBody.Builder(); if (null != formBodyMap && !formBodyMap.isEmpty()){ for (String key : formBodyMap.keySet()){ formBuilder.add(key, formBodyMap.get(key)); } } return formBuilder.build(); } /** * 把map转换成get url上的参数,?key=value&key=value * @param parameter 请求参数 map * @return HttpUrl.Builder */ static HttpUrl.Builder generateDoGetRequestParameter(String url, Map<String, String> parameter) { HttpUrl.Builder httpBuilder = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder(); if (parameter != null) { for (Map.Entry<String, String> param : parameter.entrySet()) { httpBuilder.addQueryParameter(param.getKey(), param.getValue()); } } return httpBuilder; } /** * 获取请求的响应信息,同步的 * @param client OkHttpClient * @param ok_request Request * @return 请求的响应信息 */ private static String getResponseMessage(OkHttpClient client, Request ok_request) { Call call = client.newCall(ok_request); String resultString = ""; try { //发起请求,会阻塞调用线程 Response response = call.execute(); resultString = new String(Objects.requireNonNull(response.body()).bytes(), StandardCharsets.UTF_8); log.info("http请求返回状态码:" + response.code()); //log.info("http请求返回结果:" + resultString); response.close(); } catch (IOException e) { e.printStackTrace(); } return resultString; } /** * 获取 OkHttpClient * @param isSkipHttpsSSL 是否跳过https的ssl证书认证,默认跳过 * @return OkHttpClient */ static OkHttpClient getOkHttpClient(boolean isSkipHttpsSSL) { OkHttpClient.Builder builder = new OkHttpClient.Builder(); //指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间 builder.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS); //这个值从调用call.execute();和enqueue();这两个方法开始计时,时间到后网络还未请求完成将调用cancel(); builder.callTimeout(CALL_TIMEOUT, TimeUnit.SECONDS); //如果设置了这个值会定时的向服务器发送一个消息来保持长连接 builder.pingInterval(PING_INTERVAL_TIMEOUT, TimeUnit.SECONDS); //所有的sink都被封装了一个超时机制,需要在我们设置的时间内读取SIZE(8k)的数据,如果无法完成即为超时 builder.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS); //所有的sink都被封装了一个超时机制,需要在我们设置的时间内写出TIMEOUT_WRITE_SIZE(64k)的数据,如果无法完成即为超时 builder.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS); //日志拦截器 builder.addInterceptor(new LoggingInterceptor()); //跳过https的ssl证书认证 if (isSkipHttpsSSL){ builder.sslSocketFactory(Objects.requireNonNull(TrustAllHosts.trustAllHosts()), TrustAllHosts.getX509TrustManager()); builder.hostnameVerifier(TrustAllHosts.DO_NOT_VERIFY); } return builder.build(); } }
package com.chenwc.okhttp3.utils; import com.chenwc.okhttp3.exception.MyException; import okhttp3.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Objects; /** * okhttp3 日志拦截器 * @author chenwc */ public class LoggingInterceptor implements Interceptor { private final static Logger log = LoggerFactory.getLogger(LoggingInterceptor.class); @Override public Response intercept(Chain chain) throws IOException { //这个chain里面包含了request和response,所以你要什么都可以从这里拿 Response response = null; try { Request request = chain.request(); long t1 = System.nanoTime();//请求发起的时间 log.info("------------------------开始发起请求----------------------"); log.info("Request URL: {}", request.url()); String method = request.method(); log.info("Request Method: {}", method); if ("POST".equals(method)){ log.debug("contentLength: {}", Objects.requireNonNull(request.body()).contentLength()); log.debug("RequestBody: {}", getRequestBody(request)); } Headers requestHeaders = request.headers(); for (int i = 0; i < requestHeaders.size(); i++) { log.debug(requestHeaders.name(i) + ":" + requestHeaders.value(i)); } log.info("-------------------------请求结束------------------------"); log.info("------------------------开始接收响应----------------------"); response = chain.proceed(request); long t2 = System.nanoTime();//收到响应的时间 log.info("Response URL: {}", response.request().url()); log.debug("ResponseBody: {}", getResponseBody(response)); log.info("请求耗时: {} ms", (t2 - t1) / 1e6d); log.info(response.protocol() + " " +response.code() + " " + response.message()); Headers responseHeaders = response.headers(); for (int i = 0; i < responseHeaders.size(); i++) { log.debug(responseHeaders.name(i) + ":" + responseHeaders.value(i)); } log.info("-------------------------响应结束------------------------"); } catch (MyException e) { e.printStackTrace(); } assert response != null; return response; } /** * 获取请求体信息 * @param request 请求 * @return 请求体 * @throws IOException 异常信息 */ public static String getRequestBody(Request request) throws IOException, MyException { okhttp3.RequestBody requestBody = request.body(); if (null == requestBody){ throw new MyException("request 取到的 RequestBody 为 null!"); } okio.Buffer buffer = new okio.Buffer(); requestBody.writeTo(buffer); // 编码 设为UTF-8 Charset charset = StandardCharsets.UTF_8; MediaType contentType = requestBody.contentType(); if (contentType != null) { charset = contentType.charset(StandardCharsets.UTF_8); } if (null == charset){ throw new MyException("request 取到的 RequestBody 的 contentType 为 null!"); } // 拿到request return buffer.readString(charset); } /** * 获取响应信息 * @param response 响应 * @return 响应信息 * @throws IOException 异常信息 */ public static String getResponseBody(okhttp3.Response response) throws IOException, MyException { ResponseBody responseBody = response.body(); if (null == responseBody){ throw new MyException("response 取到的 ResponseBody 为 null!"); } okio.BufferedSource source = responseBody.source(); // Buffer the entire body. source.request(Long.MAX_VALUE); @SuppressWarnings("deprecation") okio.Buffer buffer = source.buffer(); Charset charset = StandardCharsets.UTF_8; MediaType contentType = responseBody.contentType(); if (contentType != null) { charset = contentType.charset(StandardCharsets.UTF_8); } if (null == charset){ throw new MyException("response 取到的 ResponseBody 的 contentType 为 null!"); } return buffer.clone().readString(charset); } }
log4j.rootLogger=info,stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %-5p %-5L --- [%-5t] %-10c : %m %n
package com.chenwc.okhttp3.exception; public class MyException extends Exception { public MyException(String message) { super(message); } }
package com.chenwc.okhttp3.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.*; import java.security.SecureRandom; import java.security.cert.X509Certificate; /** * 绕过HTTPS的SSL验证 * @author chenwc */ public class TrustAllHosts { private static final Logger log = LoggerFactory.getLogger(TrustAllHosts.class); /** * 覆盖java默认的证书验证 */ public static final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; } public void checkClientTrusted(X509Certificate[] chain, String authType) { } public void checkServerTrusted(X509Certificate[] chain, String authType) { } }}; /** * 获取 X509TrustManager * @return X509TrustManager */ public static X509TrustManager getX509TrustManager() { return new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }; } /** * 设置不验证主机 */ public static final HostnameVerifier DO_NOT_VERIFY = (hostname, session) -> true; /** * 信任所有 * * @return SSLSocketFactory */ public static SSLSocketFactory trustAllHosts() { try { //设置HTTPS的加密算法名称 SSLContext sc = SSLContext.getInstance(HttpsAlgorithmEnum.TLSV_1_2.getAlgorithm()); sc.init(null, trustAllCerts, new SecureRandom()); SSLSocketFactory newFactory = sc.getSocketFactory(); log.info("SSLContext Version: {}", sc.getProtocol()); return newFactory; } catch (Exception e) { e.printStackTrace(); } return null; } }
package com.chenwc.okhttp3.utils; /** * TLS/SSL 协议枚举 * @author chenwc */ public enum HttpsAlgorithmEnum { SSL("SSL"), TLS("TLS"), TLSV_1_1("TLSv1.1"), TLSV_1_2("TLSv1.2"); //JDK8不支持TLSv1.3 //TLSV_1_3("TLSv1.3"); private String algorithm; HttpsAlgorithmEnum(String algorithm) { this.algorithm = algorithm; } public String getAlgorithm() { return algorithm; } public void setAlgorithm(String algorithm) { this.algorithm = algorithm; } /** * 根据算法名称获取 HttpsAlgorithmEnum * @param algo 算法名称 * @return HttpsAlgorithmEnum */ public HttpsAlgorithmEnum getAlgorithmEnum(String algo) { for (HttpsAlgorithmEnum algorithmEnum : HttpsAlgorithmEnum.values()) { if (algo.equals(algorithmEnum.getAlgorithm())) { return algorithmEnum; } } return null; } }
package com.chenwc.okhttp3.utils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import okhttp3.Call; import okhttp3.Callback; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okio.BufferedSink; import okio.Okio; import okio.Sink; /** * 文件下载工具类 * @author CalvinChan */ public class DownloadUtil { private static final Logger log = LoggerFactory.getLogger(DownloadUtil.class); /** * 下载监听器 * @author CalvinChan * */ public interface OnDownloadListener { /** * 下载成功之后的文件 */ void onDownloadSuccess(File file); /** * 下载进度 */ void onDownloading(int progress); /** * 下载异常信息 */ void onDownloadFailed(Exception e); } /** * 使用Sink下载 * @param url 下载连接 * @param destFileDir 下载的文件储存目录 * @param destFileName 下载文件名称,后面记得拼接后缀 * @param listener 下载监听器 */ public void downloadBySink(String url, String destFileDir, String destFileName, OnDownloadListener listener) { OkHttpClient client = RestMock.getOkHttpClient(url.startsWith("https")); Request ok_request = RestMock.getRequestBuilder(null) .url(RestMock.generateDoGetRequestParameter(url, null).build()).get().build(); // 异步请求 client.newCall(ok_request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { // 下载失败监听回调 listener.onDownloadFailed(e); } @Override public void onResponse(Call call, Response response) throws IOException { // 储存下载文件的目录 File dir = new File(destFileDir); if (!dir.exists()) { dir.mkdirs(); } File file = new File(dir, destFileName); Sink sink = null; BufferedSink bufferedSink = null; try { sink = Okio.sink(file); bufferedSink = Okio.buffer(sink); bufferedSink.writeAll(response.body().source()); bufferedSink.close(); listener.onDownloadSuccess(file); } catch (Exception e) { log.error("error:{}", e); listener.onDownloadFailed(e); } finally { if (bufferedSink != null) { bufferedSink.close(); } } } }); } /** * 异步下载 * @param url 下载连接 * @param destFileDir 下载的文件储存目录 * @param destFileName 下载文件名称,后面记得拼接后缀 * @param listener 下载监听器 */ public void downloadByAsync(String url, String destFileDir, String destFileName, OnDownloadListener listener) { OkHttpClient client = RestMock.getOkHttpClient(url.startsWith("https")); Request ok_request = RestMock.getRequestBuilder(null) .url(RestMock.generateDoGetRequestParameter(url, null).build()).get().build(); // 异步请求 client.newCall(ok_request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { // 下载失败监听回调 listener.onDownloadFailed(e); } @Override public void onResponse(Call call, Response response) throws IOException { InputStream is = null; byte[] buf = new byte[4096]; int len = 0; FileOutputStream fos = null; // 储存下载文件的目录 File dir = new File(destFileDir); if (!dir.exists()) { dir.mkdirs(); } File file = new File(dir, destFileName); try { is = response.body().byteStream(); fos = new FileOutputStream(file); int size = 0; long total = response.body().contentLength(); while ((size = is.read(buf)) != -1) { len += size; fos.write(buf, 0, size); int process = (int) Math.floor(((double) len / total) * 100); // 控制台打印文件下载的百分比情况 listener.onDownloading(process); } fos.flush(); // 下载完成 listener.onDownloadSuccess(file); } catch (Exception e) { log.error("error:{}", e); listener.onDownloadFailed(e); } finally { try { if (is != null) { is.close(); } if (fos != null) { fos.close(); } } catch (IOException e) { e.printStackTrace(); } } } }); } /** * 同步下载 * @param url 下载连接 * @param destFileDir 下载的文件储存目录 * @param destFileName 下载文件名称,后面记得拼接后缀 */ public void downloadBySysc(String url, String destFileDir, String destFileName) { OkHttpClient client = RestMock.getOkHttpClient(url.startsWith("https")); Request ok_request = RestMock.getRequestBuilder(null) .url(RestMock.generateDoGetRequestParameter(url, null).build()).get().build(); // 异步请求 Response response = null; InputStream is = null; byte[] buf = new byte[4096]; int len = 0; FileOutputStream fos = null; try { response = client.newCall(ok_request).execute(); if (response.isSuccessful()) { // 储存下载文件的目录 File dir = new File(destFileDir); if (!dir.exists()) { dir.mkdirs(); } File file = new File(dir, destFileName); is = response.body().byteStream(); fos = new FileOutputStream(file); int size = 0; long total = response.body().contentLength(); while ((size = is.read(buf)) != -1) { len += size; fos.write(buf, 0, size); int process = (int) Math.floor(((double) len / total) * 100); log.info("下载进度:" + process); } fos.flush(); log.info("下载完成"); } else { throw new IOException("Unexpected code " + response); } } catch (IOException e) { log.error("error:{}", e); e.printStackTrace(); } finally { try { if (is != null) { is.close(); } if (fos != null) { fos.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
测试文件下载
package com.chenwc.okhttp3; import java.io.File; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.chenwc.okhttp3.utils.DownloadUtil; public class Main { private final static Logger log = LoggerFactory.getLogger(Main.class); public static void main(String[] args) { // TODO 自动生成的方法存根 DownloadUtil downloadUtil = new DownloadUtil(); //同步下载 downloadUtil.downloadBySysc("https://repo1.maven.org/maven2/commons-codec/commons-codec/1.11/commons-codec-1.11.jar", "D:\\Downloads\\", "commons-codec-1.11.jar"); //异步下载 downloadUtil.downloadByAsync("https://repo1.maven.org/maven2/commons-codec/commons-codec/1.11/commons-codec-1.11.jar", "D:\\Downloads\\", "commons-codec-1.11.jar", new DownloadUtil.OnDownloadListener() { @Override public void onDownloadSuccess(File file) { log.info("下载完成"); } @Override public void onDownloading(int progress) { log.info("下载进度:"+progress); } @Override public void onDownloadFailed(Exception e) { //下载异常进行相关提示操作 log.error("下载出错", e); } }); } }