在springboot框架中使用okhttp实现post和get请求,有时候请求时间过长,可以使用同步和异步的方式,同步请求时间过长可以使用单独线程,避免主页面卡顿。
本文主要对Okhttp进行二次封装,方便在工程中直接调用。
okhttp有一些基于kotlin的代码,还有引入一个kotlin的资源:
com.squareup.okhttp3
okhttp
4.7.2
com.squareup.okio
okio
2.7.0-alpha.lockfree.2
org.jetbrains.kotlin
kotlin-stdlib
1.3.50
此类用于处理Okhttp的返回结果,分为完成和失败。
public interface ServiceListener {
/**
* onCompleted
* @param data
*/
void onCompleted(String data);
/**
* onError
* @param msg
*/
void onError(String msg);
}
创建上传和下载的回调类,用于接收上传文件和下载文件的结果。
public interface FileUploadListener {
/**
* 下载完成回调
*
* @param fileUploadResponse 下载完成信息
*/
void onCompleted(FileUploadResponse fileUploadResponse);
/**
* 下载错误回调
*
* @param msg 下载错误信息
*/
void onError(String msg);
}
public interface FileDownloadListener {
/**
* 下载成功之后的文件
*
* @param file 下载的文件
*/
void onDownloadSuccess(File file);
/**
* 下载进度
*
* @param progress 下载进度
*/
void onDownloading(int progress);
/**
* 下载异常信息
*
* @param e 异常
*/
void onDownloadFailed(Exception e);
}
此类用于二次封装okhttp方法,其中包括port,get,上传下载,同步异步等。具体每个方法都有注释,就不特别说明了。
以上两个类一般放在commom包中,以便在业务层调用。
public class OkHttpUtils {
private static final Logger log = LoggerFactory.getLogger(OkHttpUtils.class);
private static final OkHttpClient OK_HTTP_CLIENT;
private static final CloseableHttpClient CLOSEABLE_HTTP_CLIENT;
private static final String STATUS_CODE = "HTTP STATUS CODE:";
public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");
public static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
static {
// 创建okHttpClient
OK_HTTP_CLIENT = new OkHttpClient
.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
// 创建closeableHttpClient
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
CLOSEABLE_HTTP_CLIENT = httpClientBuilder.build();
}
/**
* get请求
*/
public static String get(String url) throws IOException {
String result = "";
Request request = new Request.Builder().url(url).build();
try (Response response = OK_HTTP_CLIENT.newCall(request).execute()) {
if (response.isSuccessful()) {
result = response.body().string();
}
}
return result;
}
/**
* post请求,body通过form格式请求
*
* @param url 请求地址
* @param authorization 请求头验证
* @param bodyParamMap 请求Body
*/
public static String post(String url, String authorization, Map bodyParamMap) throws IOException {
String result = "";
FormBody.Builder builder = new FormBody.Builder();
if (bodyParamMap != null && bodyParamMap.size() > 0) {
for (String key : bodyParamMap.keySet()) {
builder.add(key, bodyParamMap.get(key));
}
}
RequestBody formBody = builder.build();
Request request = new Request
.Builder()
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("Authorization", authorization)
.url(url)
.post(formBody)
.build();
try (Response response = OK_HTTP_CLIENT.newCall(request).execute();
InputStream inputStream = response.body().byteStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
log.debug(STATUS_CODE + response.code());
StringBuilder buffer = new StringBuilder();
String str;
while ((str = br.readLine()) != null) {
buffer.append(str);
}
result = buffer.toString();
}
return result;
}
/**
* post请求,body通过json格式请求
*
* @param url 请求地址
* @param authorization 请求头验证,取消
* @param sRequestBody 请求Body
*/
public static String post(String url, String authorization, String sRequestBody) throws IOException {
String result = "";
RequestBody body = RequestBody.create(sRequestBody,MEDIA_TYPE_JSON);
Request request = new Request.Builder()
.url(url)
.addHeader("Authorization", authorization)
.post(body)
.build();
try (Response response = OK_HTTP_CLIENT.newCall(request).execute()) {
log.debug(STATUS_CODE + response.code());
if (response.isSuccessful()) {
result = response.body().string();
}
}
return result;
}
/**
* 上传单个文件
* 同步请求,form-data方式
*
* @param url 请求地址
* @param authorization 请求头验证
* @param file 文件
*/
public static String uploadFileSynchronize(String url, String authorization, File file) throws IOException {
String result = "";
RequestBody fileBody = RequestBody.create(file, MediaType.parse("application/octet-stream"));
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("mfile", file.getName(), fileBody)
.addFormDataPart("type", "png")
.build();
Request request = new Request.Builder()
.url(url)
.addHeader("content-type", "multipart/form-data")
.addHeader("Authorization", authorization)
.post(requestBody)
.build();
try (Response response = OK_HTTP_CLIENT.newCall(request).execute()) {
log.debug(STATUS_CODE + response.code());
if (response.isSuccessful()) {
result = Objects.requireNonNull(response.body()).string();
}
}
return result;
}
/**
* post请求,上传单个文件
* 异步请求
* 此方法时间可能较长,使用单独线程将结果放在回调中返回
*
* @param url 请求地址
* @param authorization 请求头验证
* @param file 文件
* @param serviceListener 请求结果回调
*/
public static void uploadFileAsynchronous(String url, String authorization, File file, ServiceListener serviceListener) {
RequestBody fileBody = RequestBody.create(file, MediaType.parse("application/octet-stream"));
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("mfile", file.getName(), fileBody)
.addFormDataPart("type", "png")
.build();
Request request = new Request.Builder()
.url(url)
.addHeader("content-type", "multipart/form-data")
.addHeader("Authorization", authorization)
.post(requestBody)
.build();
OK_HTTP_CLIENT.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
serviceListener.onError(e.getMessage());
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
try (InputStream inputStream = response.body().byteStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
log.debug(STATUS_CODE + response.code());
StringBuilder buffer = new StringBuilder();
String str;
while ((str = br.readLine()) != null) {
buffer.append(str);
}
serviceListener.onCompleted(buffer.toString());
} catch (IOException e) {
log.error(e.toString());
serviceListener.onError(e.getMessage());
}
}
});
}
/**
* post请求,上传多个文件
* 此方法时间可能较长,使用单独线程将结果放在回调中返回
*
* @param url 请求地址
* @param authorization 请求头验证
* @param fileNames 文件完整路径
* @param serviceListener 请求结果回调
*/
public static void uploadFilesAsynchronous(String url, String authorization, List fileNames, ServiceListener serviceListener) {
MultipartBody.Builder builder = new MultipartBody.Builder();
for (String fileName : fileNames) {
File file = new File(fileName);
RequestBody requestBody = RequestBody.create(file, MediaType.parse("application/octet-stream"));
builder.addFormDataPart("mfile", file.getName(), requestBody);
}
Request request = new Request.Builder()
.url(url)
.addHeader("content-type", "multipart/form-data")
.addHeader("Authorization", authorization)
.post(builder.build())
.build();
OK_HTTP_CLIENT.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
serviceListener.onError(e.getMessage());
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
try (InputStream inputStream = response.body().byteStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
log.debug(STATUS_CODE + response.code());
StringBuilder buffer = new StringBuilder();
String str;
while ((str = br.readLine()) != null) {
buffer.append(str);
}
serviceListener.onCompleted(buffer.toString());
} catch (IOException e) {
log.error(e.toString());
serviceListener.onError(e.getMessage());
}
}
});
}
/**
* 下载文件
* 异步请求
*
* @param url 下载连接
* @param destFileDir 下载的文件储存目录
* @param destFileName 下载文件名称,后面记得拼接后缀
* @param listener 下载监听
*/
public static void downloadFilesAsynchronous(String url, String destFileDir, String destFileName, FileDownloadListener listener) {
Request request = new Request.Builder()
.url(url)
.build();
//异步请求
OK_HTTP_CLIENT.newCall(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[2048];
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();
long total = response.body().contentLength();
fos = new FileOutputStream(file);
long sum = 0;
while ((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
sum += len;
int progress = (int) (sum * 1.0f / total * 100);
//下载中更新进度条
listener.onDownloading(progress);
}
fos.flush();
//下载完成
listener.onDownloadSuccess(file);
} catch (Exception e) {
listener.onDownloadFailed(e);
} finally {
try {
if (is != null) {
is.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
}
}
}
});
}
/**
* 下载文件
* 同步请求
*
* @param url 下载连接
* @param destFileDir 下载的文件储存目录
* @param destFileName 下载文件名称,后面记得拼接后缀
*/
public static String downloadSynchronize(String url, String destFileDir, String destFileName) throws IOException {
String result = "";
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = OK_HTTP_CLIENT.newCall(request).execute()) {
InputStream is = null;
byte[] buf = new byte[2048];
int len = 0;
FileOutputStream fos = null;
//储存下载文件的目录
log.info("filePath:" + destFileDir + destFileName);
File dir = new File(destFileDir);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, destFileName);
try {
is = response.body().byteStream();
long total = response.body().contentLength();
fos = new FileOutputStream(file);
long sum = 0;
while ((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
sum += len;
int progress = (int) (sum * 1.0f / total * 100);
//下载中更新进度条
}
fos.flush();
//下载完成
result = file.getPath();
log.info("downloadSynchronize finish:{}", file.getPath());
} catch (Exception e) {
log.info("downloadSynchronize error:{}", e.getMessage());
} finally {
log.info("downloadSynchronize finally");
try {
if (is != null) {
is.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
log.info("downloadSynchronize IOException:{}", e.getMessage());
}
}
}
return result;
}
/**
* 根据地址获得数据的输入流,以数据流方式上传
*
* @param host 上传host
* @param imgUrl 文件url
* @param authorization 请求头验证
* @param serviceListener 请求结果回调
*/
public static void uploadFileStreamAsynchronous(String host, String imgUrl, String authorization, ServiceListener serviceListener) {
HttpPost post = new HttpPost(host);
InputStream inputStream = null;
post.setHeader("content-type", "application/octet-stream");
post.setHeader("Authorization", authorization);
try {
inputStream = getInputStreamByUrl(imgUrl);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.addBinaryBody("mfile", inputStream, ContentType.create("multipart/form-data"), "wKgBP2CwWGuAPWdJAAvySPq-Jpo973.jpg");
//4)构建请求参数 普通表单项
HttpEntity entity = builder.build();
post.setEntity(entity);
//发送请求
HttpResponse response = CLOSEABLE_HTTP_CLIENT.execute(post);
entity = response.getEntity();
if (entity != null) {
inputStream = entity.getContent();
//转换为字节输入流
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, Consts.UTF_8));
String body = null;
while ((body = br.readLine()) != null) {
log.info("uploadFile:{}" , body);
serviceListener.onCompleted(body);
}
}
} catch (FileNotFoundException e) {
log.error("FileNotFoundException:{}" , e.getMessage());
e.printStackTrace();
serviceListener.onError(e.getMessage());
} catch (ClientProtocolException e) {
log.error("ClientProtocolException:{}" , e.getMessage());
e.printStackTrace();
serviceListener.onError(e.getMessage());
} catch (IOException e) {
log.error("IOException:{}" , e.getMessage());
e.printStackTrace();
serviceListener.onError(e.getMessage());
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 根据地址获得数据的输入流
*
* @param strUrl 网络连接地址
* @return url的输入流
*/
public static InputStream getInputStreamByUrl(String strUrl) {
HttpURLConnection conn = null;
try {
URL url = new URL(strUrl);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(20 * 1000);
final ByteArrayOutputStream output = new ByteArrayOutputStream();
IOUtils.copy(conn.getInputStream(), output);
return new ByteArrayInputStream(output.toByteArray());
} catch (Exception e) {
log.error(e + "");
} finally {
try {
if (conn != null) {
conn.disconnect();
}
} catch (Exception e) {
log.error(e + "");
}
}
return null;
}
}
由于是基于springboot框架,业务层一般在service层,添加方法,在impl里实现具体业务。
如果不需要token,可以把access_token参数去掉。
service类:
/**
* 上传单个文件
* 异步请求,binary-stream方式
*
* @param fileUrl 文件url
* @param serviceListener 结果回调
*/
public void uploadFileStreamAsynchronous(String fileUrl, ServiceListener serviceListener);
impl类实现:
@Override
public void uploadFileStreamAsynchronous(String fileUrl, ServiceListener serviceListener) {
OkHttpUtils.uploadFileStreamAsynchronous(uploadFilesUrl, fileUrl, access_token, new ServiceListener() {
@Override
public void onCompleted(String data) {
serviceListener.onCompleted(data);
}
@Override
public void onError(String msg) {
serviceListener.onError(msg);
}
});
}
如果不需要token,可以把access_token参数去掉。
service类:
/**
* 上传单个文件
* 异步请求,form-data方式
*
* @param filePath 文件路径
* @param fileUploadListener 结果回调
* @throws IOException 异常
*/
public void uploadFileAsynchronous(String filePath, FileUploadListener fileUploadListener) throws IOException;
impl类实现:
@Override
public void uploadFileAsynchronous(String filePath, FileUploadListener fileUploadListener) {
File file = new File(filePath);
OkHttpUtils.uploadFileAsynchronous(uploadFilesUrl, access_token, file, new ServiceListener() {
@Override
public void onCompleted(String data) {
FileUploadResponse fileUploadResponse = JSON.parseObject(data, new TypeReference() {
});
if (ApiConstants.CODE_SUCCESS.equals(fileUploadResponse.getErrcode())) {
fileUploadListener.onCompleted(fileUploadResponse);
} else {
fileUploadListener.onError(data);
}
}
@Override
public void onError(String msg) {
fileUploadListener.onError(msg);
}
});
}
service类:
/**
* 下载文件方法
* 同步请求
*
* @param url 下载连接
* @param destFileDir 下载的文件储存目录
* @param destFileName 下载文件名称,后面记得拼接后缀
* @return String 返回结果
* @throws Exception 异常
*/
public String downloadSynchronize(String url, String destFileDir, String destFileName) throws Exception;
impl类实现:
@Override
public String downloadSynchronize(String url, String destFileDir, String destFileName) throws Exception {
String result = "";
result = OkHttpUtils.downloadSynchronize(url, destFileDir, destFileName);
return result;
}
service类:
/**
* 下载文件方法
* 异步请求
*
* @param url 下载连接
* @param destFileDir 下载的文件储存目录
* @param destFileName 下载文件名称,后面记得拼接后缀
* @param listener 下载监听
*/
public void downloadFilesAsynchronous(String url, String destFileDir, String destFileName, FileDownloadListener listener);
impl类实现:
@Override
public void downloadFilesAsynchronous(String url, String destFileDir, String destFileName, FileDownloadListener fileDownloadListener) {
OkHttpUtils.downloadFilesAsynchronous(url, destFileDir, destFileName, new FileDownloadListener() {
@Override
public void onDownloadSuccess(File file) {
log.info("downloadFiles onDownloadSuccess:{}", file.getName());
fileDownloadListener.onDownloadSuccess(file);
}
@Override
public void onDownloading(int progress) {
fileDownloadListener.onDownloading(progress);
}
@Override
public void onDownloadFailed(Exception e) {
log.info("downloadFiles onDownloadFailed:{}", e.toString());
fileDownloadListener.onDownloadFailed(e);
}
});
}
如果不需要token,可以把access_token参数去掉。
service类:
/**
* POST
* 同步请求
*
* @param sRequestBody 传入信息的String
* @return String 返回结果
* @throws IOException 异常
*/
public String post(String sRequestBody) throws IOException;
impl类实现:
@Override
public String post(String sRequestBody) throws IOException {
String result = "";
result = OkHttpUtils.post(url, access_token, sRequestBody);
return result;
}
如果不需要token,可以把access_token参数去掉。
service类:
/**
* GET
* 同步请求
*
* @param sRequestBody 传入信息的String
* @return String 返回结果
* @throws IOException 异常
*/
public String get(String url) throws IOException;
impl类实现:
@Override
public String get(String url) throws IOException {
String result = "";
result = OkHttpUtils.get(url);
return result;
}
到这里基本上常用的POST和GET上传下载的功能就实现了。使用请求时还需要验证headers,token等操作,还有上传下载进行同步操作时,如果文件比较大,直接在主线程操作会使页面卡住,等待完成才能操作,不是很友好,这里可以使用线程操作,这些等之后有时间整理下再写一篇。