<?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);
}
});
}
}