001/** 002 * Copyright (c) 2025-2026, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.gnu.org/licenses/lgpl-3.0.txt 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package dev.tinyflow.core.util; 017 018import okhttp3.*; 019import okio.BufferedSink; 020import org.jetbrains.annotations.NotNull; 021import org.slf4j.Logger; 022import org.slf4j.LoggerFactory; 023 024import java.io.File; 025import java.io.IOException; 026import java.io.InputStream; 027import java.util.Map; 028 029public class OKHttpClientWrapper { 030 private static final Logger LOG = LoggerFactory.getLogger(OKHttpClientWrapper.class); 031 private static final MediaType JSON_TYPE = MediaType.parse("application/json; charset=utf-8"); 032 033 private final OkHttpClient okHttpClient; 034 035 public OKHttpClientWrapper() { 036 this(OkHttpClientUtil.buildDefaultClient()); 037 } 038 039 public OKHttpClientWrapper(OkHttpClient okHttpClient) { 040 this.okHttpClient = okHttpClient; 041 } 042 043 public String get(String url) { 044 return executeString(url, "GET", null, null); 045 } 046 047 public byte[] getBytes(String url) { 048 return executeBytes(url, "GET", null, null); 049 } 050 051 public String get(String url, Map<String, String> headers) { 052 return executeString(url, "GET", headers, null); 053 } 054 055 public String post(String url, Map<String, String> headers, String payload) { 056 return executeString(url, "POST", headers, payload); 057 } 058 059 public byte[] postBytes(String url, Map<String, String> headers, String payload) { 060 return executeBytes(url, "POST", headers, payload); 061 } 062 063 public String put(String url, Map<String, String> headers, String payload) { 064 return executeString(url, "PUT", headers, payload); 065 } 066 067 public String delete(String url, Map<String, String> headers, String payload) { 068 return executeString(url, "DELETE", headers, payload); 069 } 070 071 public String multipartString(String url, Map<String, String> headers, Map<String, Object> payload) { 072 try (Response response = multipart(url, headers, payload); 073 ResponseBody body = response.body()) { 074 if (body != null) { 075 return body.string(); 076 } 077 } catch (Exception e) { 078 LOG.error(e.toString(), e); 079 } 080 return null; 081 } 082 083 084 public byte[] multipartBytes(String url, Map<String, String> headers, Map<String, Object> payload) { 085 try (Response response = multipart(url, headers, payload); 086 ResponseBody body = response.body()) { 087 if (body != null) { 088 return body.bytes(); 089 } 090 } catch (Exception e) { 091 LOG.error(e.toString(), e); 092 } 093 return null; 094 } 095 096 097 public Response multipart(String url, Map<String, String> headers, Map<String, Object> payload) throws IOException { 098 Request.Builder builder = new Request.Builder() 099 .url(url); 100 101 if (headers != null && !headers.isEmpty()) { 102 headers.forEach(builder::addHeader); 103 } 104 105 MultipartBody.Builder mbBuilder = new MultipartBody.Builder() 106 .setType(MultipartBody.FORM); 107 payload.forEach((s, o) -> { 108 if (o instanceof File) { 109 File f = (File) o; 110 RequestBody body = RequestBody.create(f, MediaType.parse("application/octet-stream")); 111 mbBuilder.addFormDataPart(s, f.getName(), body); 112 } else if (o instanceof InputStream) { 113 RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), (InputStream) o); 114 mbBuilder.addFormDataPart(s, s, body); 115 } else if (o instanceof byte[]) { 116 mbBuilder.addFormDataPart(s, s, RequestBody.create((byte[]) o)); 117 } else { 118 mbBuilder.addFormDataPart(s, String.valueOf(o)); 119 } 120 }); 121 122 MultipartBody multipartBody = mbBuilder.build(); 123 Request request = builder.post(multipartBody).build(); 124 return okHttpClient.newCall(request).execute(); 125 } 126 127 128 public String executeString(String url, String method, Map<String, String> headers, String payload) { 129 try (Response response = execute0(url, method, headers, payload); 130 ResponseBody body = response.body()) { 131 if (body != null) { 132 return body.string(); 133 } 134 } catch (Exception e) { 135 LOG.error(e.toString(), e); 136 } 137 return null; 138 } 139 140 141 public byte[] executeBytes(String url, String method, Map<String, String> headers, String payload) { 142 try (Response response = execute0(url, method, headers, payload); 143 ResponseBody body = response.body()) { 144 if (body != null) { 145 return body.bytes(); 146 } 147 } catch (Exception e) { 148 LOG.error(e.toString(), e); 149 } 150 return null; 151 } 152 153 154 private Response execute0(String url, String method, Map<String, String> headers, String payload) throws IOException { 155 Request.Builder builder = new Request.Builder() 156 .url(url); 157 158 if (headers != null && !headers.isEmpty()) { 159 headers.forEach(builder::addHeader); 160 } 161 162 Request request; 163 if ("GET".equalsIgnoreCase(method)) { 164 request = builder.method(method, null).build(); 165 } else { 166 RequestBody body = RequestBody.create(payload, JSON_TYPE); 167 request = builder.method(method, body).build(); 168 } 169 170 return okHttpClient.newCall(request).execute(); 171 } 172 173 174 public static class InputStreamRequestBody extends RequestBody { 175 private final InputStream inputStream; 176 private final MediaType contentType; 177 178 public InputStreamRequestBody(MediaType contentType, InputStream inputStream) { 179 if (inputStream == null) throw new NullPointerException("inputStream == null"); 180 this.contentType = contentType; 181 this.inputStream = inputStream; 182 } 183 184 @Override 185 public MediaType contentType() { 186 return contentType; 187 } 188 189 @Override 190 public long contentLength() throws IOException { 191 return inputStream.available() == 0 ? -1 : inputStream.available(); 192 } 193 194 @Override 195 public void writeTo(@NotNull BufferedSink sink) throws IOException { 196 IOUtil.copy(inputStream, sink); 197 } 198 } 199}