参考链接: https://blog.csdn.net/riemann_/article/details/90539829.
参考链接: 使用HttpURlConnection 发送POST请求上传文件(带参数).
使用这些http客户端时注意tcp链接数的问题
参考链接: 高并发下对接第三方请求时http请求的问题及处理方式.
通过Java.net.HttpURLConnection实现
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class MyHttpUtil {
private static final int TIME_OUT = 5 * 1000; //超时时间
private static final String GET = "GET"; //GET请求
private static final String POST = "POST"; //GET请求
private static final String CHARSET = "UTF-8"; //编码格式
private static final String PREFIX = "--"; //前缀
private static final String LINE_END = "\r\n"; //换行
/**
* 对post参数进行编码处理
*/
private static StringBuilder getStrParams(Map<String, String> strParams, String BOUNDARY) {
StringBuilder strSb = new StringBuilder();
for (Map.Entry<String, String> entry : strParams.entrySet()) {
strSb.append(PREFIX)
.append(BOUNDARY)
.append(LINE_END)
.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + LINE_END)
.append("Content-Type: text/plain; charset=" + CHARSET + LINE_END)
.append("Content-Transfer-Encoding: 8bit" + LINE_END)
.append(LINE_END)// 参数头设置完以后需要两个换行,然后才是参数内容
.append(entry.getValue())
.append(LINE_END);
}
return strSb;
}
public static String upload(String url, Map<String, String> strParams, File file) {
String boundary = UUID.randomUUID().toString(); // 文件边界
String filename = file.getName();
StringBuffer result = new StringBuffer();
HttpURLConnection connection = null;
try {
URL url1 = new URL(url);
connection = (HttpURLConnection) url1.openConnection();
connection.setRequestMethod(POST);
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setConnectTimeout(TIME_OUT);
connection.setReadTimeout(TIME_OUT);
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("Charset", CHARSET);
connection.setRequestProperty("Content-Type", "multipart/form-data" + ";boundary=" + boundary);
connection.connect();
try (DataOutputStream dos = new DataOutputStream(connection.getOutputStream());
InputStream is = new FileInputStream(file)) {
dos.writeBytes(getStrParams(strParams, boundary).toString());
dos.flush();
StringBuilder fileSb = new StringBuilder();
fileSb.append(PREFIX)
.append(boundary)
.append(LINE_END)
.append("Content-Disposition: form-data; name=\"file\"; filename=\""
+ filename + "\"" + LINE_END)
.append("Content-Transfer-Encoding: 8bit" + LINE_END)
.append(LINE_END);
dos.writeBytes(fileSb.toString());
dos.flush();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {
dos.write(buffer, 0, len);
}
//请求结束标志
dos.writeBytes(LINE_END + PREFIX + boundary + PREFIX + LINE_END);
dos.flush();
}
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), CHARSET))) {
String line;
while ((line = reader.readLine()) != null) {
result.append(line);
}
System.out.println(result.toString());
}
} catch (IOException e) {
e.printStackTrace();
} finally {
connection.disconnect();
}
return result.toString();
}
public static String post(String url, String content) {
StringBuffer result = new StringBuffer();
HttpURLConnection connection = null;
try {
URL url1 = new URL(url);
connection = (HttpURLConnection) url1.openConnection();
//设置请求方式
connection.setRequestMethod(POST);
//DoOutput设置是否向httpUrlConnection输出,DoInput设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
connection.setDoOutput(true);
connection.setDoInput(true);
// 设置是否允许缓存值
connection.setUseCaches(false);
//设置连接超时时间
connection.setConnectTimeout(TIME_OUT);
//设置读取超时时间
connection.setReadTimeout(TIME_OUT);
//设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
connection.connect();
try (OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), CHARSET)) {
out.append(content);
out.flush();
}
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), CHARSET))) {
String line;
while ((line = reader.readLine()) != null) {
result.append(line);
}
System.out.println(result.toString());
}
} catch (IOException e) {
e.printStackTrace();
} finally {
connection.disconnect();
}
return result.toString();
}
public static String get(String url) {
StringBuffer stringBuffer = new StringBuffer();
HttpURLConnection connection = null;
try {
URL url1 = new URL(url);
connection = (HttpURLConnection) url1.openConnection();
//设置请求方式
connection.setRequestMethod(GET);
//设置连接超时时间
connection.setReadTimeout(TIME_OUT);
//开始连接
connection.connect();
try (BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), CHARSET))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuffer.append(line);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
connection.disconnect();
}
System.out.println("GET请求返回值:" + stringBuffer.toString());
return stringBuffer.toString();
}
}
总结:
如果使用了HttpUrlConnection进行http请求
1.最好在finally中加disconnet(),以防异常时候http连接未被关闭,造成tcp连接堆积
2.并且要设置连接和读取超时时间,否则程序会一直卡在获取流这一步
3.记得关闭IO流资源
HttpClient
参考链接: HttpClient用法–这一篇全了解.
链接: 实现HttpClient重试
HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。HttpClient已经应用在很多的项目中,比如Apache Jakarta上很著名的另外两个开源项目Cactus和HTMLUnit都使用了HttpClient。
HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性,它不仅使客户端发送Http请求变得容易,而且也方便开发人员测试接口(基于Http协议的),提高了开发的效率,也方便提高代码的健壮性。
org.apache.commons.httpclient.HttpClient与org.apache.http.client.HttpClient的区别
Commons的HttpClient项目现在是生命的尽头,不再被开发, 已被Apache HttpComponents项目HttpClient和HttpCore 模组取代,提供更好的性能和更大的灵活性。
基本使用方法
<!-- 导入依赖包 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.13</version>
</dependency>
使用 HttpClient 发送请求主要分为一下几步骤:
创建 CloseableHttpClient 对象或 CloseableHttpAsyncClient 对象
创建 Http 请求对象
调用 execute 方法执行请求,如果是异步请求在执行之前需调用 start 方法
释放连接或放回线程池
参考链接: HttpClient详细使用示例.
ClosableHttpClient 同步请求
import com.alibaba.fastjson.JSON;
import com.zm.entity.User;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HttpClientUtil {
public static void get(String url) {
// 创建 Http 请求对象
HttpGet httpGet = new HttpGet(url);
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(3000)//使用连接池来管理连接,从连接池获取连接的超时时间
.setSocketTimeout(5000)//请求超时,数据传输过程中数据包之间间隔的最大时间
.setConnectTimeout(5000)//连接超时,连接建立时间,三次握手完成时间
.build();
httpGet.setConfig(requestConfig);
try (// 获得Http客户端(创建 CloseableHttpClient 对象或 CloseableHttpAsyncClient 对象)
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// 调用 execute 方法执行请求,如果是异步请求在执行之前需调用 start 方法
CloseableHttpResponse response = httpClient.execute(httpGet)) {
// 从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("响应内容为:" + EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* get请求 参数转码
*
* @return
* @throws UnsupportedEncodingException
*/
public static String getParams(Map<String, String> params){
StringBuilder strSb = new StringBuilder();
boolean index = false;
for (Map.Entry<String, String> entry : params.entrySet()) {
if (index) {
strSb.append("&");
}
strSb.append(entry.getKey())
.append("=")
.append(encode(entry.getValue()));
index = true;
}
return strSb.toString();
}
public static void get(String url, Map<String, String> params) {
// 方式一:直接拼接URL
String param = getParams(params);
// 创建 Http 请求对象
HttpGet httpGet = new HttpGet(url + "?" + param);
// 方式二:使用URI获得HttpGet
// URI uri = null;
// try {
// // 将参数放入键值对类NameValuePair中,再放入集合中
// List<NameValuePair> param = new ArrayList<>();
// for (Map.Entry<String, String> entry : params.entrySet()) {
// param.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
// }
// // 设置uri信息,并将参数集合放入uri;
// uri = new URIBuilder().setScheme("http").setHost("localhost")
// .setPort(8080).setPath("/Exception")
// .setParameters(param).build();
//
// } catch (URISyntaxException e) {
// e.printStackTrace();
// }
// // 创建Get请求
// HttpGet httpGet = new HttpGet(uri);
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(3000)//使用连接池来管理连接,从连接池获取连接的超时时间
.setSocketTimeout(5000)//请求超时,数据传输过程中数据包之间间隔的最大时间
.setConnectTimeout(5000)//连接超时,连接建立时间,三次握手完成时间
.build();
httpGet.setConfig(requestConfig);
try (// 获得Http客户端(创建 CloseableHttpClient 对象或 CloseableHttpAsyncClient 对象)
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// 调用 execute 方法执行请求,如果是异步请求在执行之前需调用 start 方法
CloseableHttpResponse response = httpClient.execute(httpGet)) {
// 从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("响应内容为:" + EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void post(String url, String params) {
// 创建Post请求
HttpPost httpPost = new HttpPost(url);
// post请求是将参数放在请求体里面传过去的;这里将entity放入post请求体中
httpPost.setEntity(new StringEntity(params, StandardCharsets.UTF_8));
httpPost.setHeader("Content-Type", "application/json;charset=utf8");
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build();
CloseableHttpResponse response = httpClient.execute(httpPost)) {
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("响应内容为:" + EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static String encode(String str) {
String encode = null;
try {
encode = URLEncoder.encode(str, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return encode;
}
public static void upload(String url, Map<String, String> params, File file) {
HttpPost httpPost = new HttpPost(url);
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
String filesKey = "file";
// (多个文件的话,使用同一个key就行,后端用数组或集合进行接收即可)
// 文件名其实是放在请求头的Content-Disposition里面进行传输的,如其值为form-data; name="files"; filename="头像.jpg"
multipartEntityBuilder.addBinaryBody(filesKey, file, ContentType.DEFAULT_BINARY, encode(file.getName()));
// 其它参数(注:自定义contentType,设置UTF-8是为了防止服务端拿到的参数出现乱码)
ContentType contentType = ContentType.create("text/plain", Charset.forName("UTF-8"));
for (Map.Entry<String, String> entry : params.entrySet()) {
multipartEntityBuilder.addTextBody(entry.getKey(), entry.getValue(), contentType);
}
HttpEntity httpEntity = multipartEntityBuilder.build();
httpPost.setEntity(httpEntity);
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build();
CloseableHttpResponse response = httpClient.execute(httpPost)) {
HttpEntity responseEntity = response.getEntity();
System.out.println("HTTPS响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
String responseStr = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
System.out.println("HTTPS响应内容为:" + responseStr);
}
} catch (ParseException | IOException e) {
e.printStackTrace();
}
}
}
CloseableHttpAsynClient 异步请求(了解)
链接: 异步请求CloseableHttpAsyncClient的使用.
OkHttp
参考链接: java使用OkHttp.
参考链接: Okhttp4 RequestBody.create()过时 解决办法.
使用 OkHttp 发送请求主要分为一下几步骤:
创建 OkHttpClient 对象
创建 Request 对象
将 Request 对象封装为 Call
通过 Call 来执行同步或异步请求,调用 execute 方法同步执行,调用 enqueue 方法异步执行
<!--导入依赖 排除android部分 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.1</version>
<exclusions>
<exclusion>
<groupId>com.google.android</groupId>
<artifactId>android</artifactId>
</exclusion>
</exclusions>
</dependency>
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import okhttp3.*;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.TimeUnit;
public class OkHttpUtil {
private static volatile OkHttpClient client;
private static final String DEFAULT_MEDIA_TYPE = "application/json;charset=utf-8";
private static final int CONNECT_TIMEOUT = 5;
private static final int READ_TIMEOUT = 7;
/**
* 单例模式 获取类实例
*
* @return client
*/
private static OkHttpClient getInstance() {
if (client == null) {
synchronized (OkHttpClient.class) {
if (client == null) {
client = new OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)// 设置连接超时时间
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)// 设置读取超时时间
.build();
}
}
}
return client;
}
public static String get(String url) {
try {
Request request = new Request.Builder().url(url).build();
Response response = getInstance().newCall(request).execute();
return handleHttpResponse(response);
} catch (Exception ex) {
return StringUtils.EMPTY;
}
}
public static String encode(String str) {
String encode = null;
try {
encode = URLEncoder.encode(str, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return encode;
}
/**
* get请求 参数转码
*
* @return
* @throws UnsupportedEncodingException
*/
public static String getParams(Map<String, String> params) {
StringBuilder strSb = new StringBuilder();
boolean index = false;
for (Map.Entry<String, String> entry : params.entrySet()) {
if (index) {
strSb.append("&");
}
strSb.append(entry.getKey())
.append("=")
.append(encode(entry.getValue()));
index = true;
}
return strSb.toString();
}
public static String get(String url, Map<String, String> params) {
try {
url = url + "?" + getParams(params);
System.out.println(url);
Request request = new Request.Builder().url(url).build();
Response response = getInstance().newCall(request).execute();
return handleHttpResponse(response);
} catch (Exception ex) {
return StringUtils.EMPTY;
}
}
public static String post(String url, String postBody) {
try {
MediaType mediaType = MediaType.Companion.parse(DEFAULT_MEDIA_TYPE);
RequestBody stringBody = RequestBody.Companion.create(postBody, mediaType);
Request request = new Request.Builder()
.url(url)
.post(stringBody)
.build();
Response response = getInstance().newCall(request).execute();
return handleHttpResponse(response);
} catch (Exception ex) {
return StringUtils.EMPTY;
}
}
public static String upload(String url, Map<String, String> params, File file) throws IOException {
MultipartBody.Builder builder = new MultipartBody.Builder();
String fileName = file.getName();
MediaType mediaType = MediaType.Companion.parse("multipart/form-data");
RequestBody fileBody = RequestBody.Companion.create(file, mediaType);
for (Map.Entry<String, String> entry : params.entrySet()) {
builder.addFormDataPart(entry.getKey(), entry.getValue()); // 上传参数
}
RequestBody requestBody = builder
.setType(MultipartBody.FORM)
.addFormDataPart("file", fileName, fileBody)
.build();
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
Response response = getInstance().newCall(request).execute();
return handleHttpResponse(response);
}
public static void main(String[] args) throws IOException {
HashMap<String, String> map = new HashMap<>();
map.put("name","小明");
System.out.println(upload("http://127.0.0.1:8080/upload", map,new File("C:\\Users\\a\\Pictures\\1.jpg")));
}
private static String handleHttpResponse(Response response) throws IOException {
if (response.body() == null) {
throw new IOException("exception in OkHttpUtil,response body is null");
}
if (!response.isSuccessful()) {
throw new RuntimeException("OkHttpUtil request failed");
}
return response.body().string();
}
}
文件和参数接收
import com.zm.entity.User;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
@RestController
public class TestController {
@PostMapping("/upload")
@ApiOperation(value = "上传文件")
public String upload(@RequestParam("file") MultipartFile file, User user) {
System.out.println(user);
String fileName = file.getOriginalFilename();// 文件名
String filePath = "F:\\img\\";// 保存图片的地址
File dest = new File(filePath + fileName);
try {
file.transferTo(dest);
return "上传成功";
} catch (IOException e) {
e.printStackTrace();
}
return "上传失败!";
}
}
RestTemplate
链接: RestTemplate与ClosableHttpClient.
Spring WebClient下封装了专门用于restful请求的RestTempate实际上内部就采用了ClosableHttpClient,对于有连接池的Client来说,最好使用单例模式,同时根据调用量配置合适的连接池大小以及配置各种超时时间等
待续。。。。。
https://www.jianshu.com/p/c852cbcf3d68
首先,明确两点:
1.http连接池不是万能的,过多的长连接会占用服务器资源,导致其他服务受阻
2.http连接池只适用于请求是经常访问同一主机(或同一个接口)的情况下
3.并发数不高的情况下资源利用率低下
那么,当你的业务符合上面3点,那么你可以考虑使用http连接池来提高服务器性能
使用http连接池的优点:
1.复用http连接,省去了tcp的3次握手和4次挥手的时间,极大降低请求响应的时间
2.自动管理tcp连接,不用人为地释放/创建连接
Forest
SpringBoot整合Forest实现调用第三方接口
https://zhengqing.blog.csdn.net/article/details/108258130
轻量级 HTTP 客户端
https://www.oschina.net/p/forest?hmsr=aladdin1e1
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/133947.html