赞
踩
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
/** * @description:发送HTTP POST请求,参数为JSON格式,并返回二进制数据。 * @author: lizhiwei * @date: 2023/12/15 15:59 * @param url 请求的URL地址。 * @param jsonParams 请求的参数,以JSON字符串形式提供。 * @param headParams 请求的头部参数,以键值对形式提供。 * @return: byte[] 服务器响应的二进制数据。 **/ public static byte[] doPostJsonForBytes(String url, String jsonParams, Map<String, String> headParams) { // 创建HTTP客户端实例 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; try { // 创建HTTP POST请求对象 HttpPost httpPost = new HttpPost(url); // 配置请求和传输超时时间 RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100000).setConnectTimeout(100000).build(); httpPost.setConfig(requestConfig); // 设置请求体为JSON字符串 httpPost.setEntity(new StringEntity(jsonParams, ContentType.APPLICATION_JSON)); // 如果存在头部参数,则添加到请求头中 if (headParams != null) { for (Map.Entry<String, String> entry : headParams.entrySet()) { httpPost.addHeader(entry.getKey(), entry.getValue()); } } // 执行请求并获取响应 response = httpClient.execute(httpPost); // 检查响应状态码 int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { throw new BusinessException(ErrorCode.SYSTEM_ERROR, "HTTP请求失败,状态码:" + statusCode); } // 获取响应二进制数据 HttpEntity entity = response.getEntity(); // String responseBody = EntityUtils.toString(entity); // log.error("看看正确的错误的,返回的有什么区别?"+responseBody); // 将响应实体转换为字节数组 byte[] responseBytes = EntityUtils.toByteArray(entity); return responseBytes; } catch (Exception e) { log.error("执行HTTP POST请求异常:", e); throw new BusinessException(ErrorCode.SYSTEM_ERROR, "执行HTTP POST请求异常:" + e.getMessage()); } finally { // 关闭资源 HttpClientUtils.closeQuietly(response); HttpClientUtils.closeQuietly(httpClient); } }
HTTP 协议简介
: HTTP(超文本传输协议)是一个用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是一个基于请求-响应模式的无状态协议,通常运行在TCP/IP协议之上。客户端和服务器模型
: 在HTTP通信中,通常有一个客户端(例如一个Web浏览器或一个HTTP客户端库,如Apache HttpClient)和一个服务器(托管Web资源的服务器)。客户端发起请求,服务器响应请求。域名解析
: 当客户端要发送HTTP请求时,首先需要解析服务器的域名(如果提供的是域名而非IP地址)。这涉及到DNS(域名系统)查询,将域名转换为IP地址。建立TCP连接
: HTTP通常运行在TCP协议之上。在发送HTTP请求之前,客户端会与服务器建立一个TCP连接。这个过程通常包括三次握手,确保双方都准备好进行数据传输。发送HTTP请求
: 一旦TCP连接建立,客户端就会通过这个连接发送一个HTTP请求。这个请求包含:
User-Agent
、Accept
、Content-Type
等。服务器处理请求
: 服务器接收到HTTP请求后,会根据请求的资源、方法和其他参数处理请求。这可能涉及到访问数据库、执行服务器端程序等操作。发送响应
: 处理请求后,服务器会向客户端发送一个HTTP响应。这个响应包含:
Content-Type
、Content-Length
等。关闭TCP连接
: 数据传输完成后,TCP连接可以被关闭,或者根据HTTP头部中的Connection
字段保持开放以供未来的请求复用。无状态性和连接管理
: HTTP是一个无状态协议,意味着每个请求都是独立的,服务器不保存两个请求之间的状态。但在现代HTTP应用中,通常使用cookies、会话等机制来维护状态。同时,持久连接、管道化等技术被用于优化连接管理和网络资源的使用。创建HTTP客户端实例:
CloseableHttpClient httpClient = HttpClients.createDefault();
这行代码使用Apache HttpClient库创建了一个CloseableHttpClient
对象。这个客户端用于执行HTTP请求。createDefault()
方法会返回一个默认配置的CloseableHttpClient
实例。
创建HTTP POST请求对象:
HttpPost httpPost = new HttpPost(url);
通过HttpPost
类创建一个POST请求对象。这个对象包含了要发送到服务器的请求信息,其中url
是请求的目标地址。
配置请求和传输超时时间:
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100000).setConnectTimeout(100000).build();
httpPost.setConfig(requestConfig);
使用RequestConfig
设置请求的超时配置。setSocketTimeout
设置从服务器读取数据的超时时间,setConnectTimeout
设置连接服务器的超时时间。这里两者都设置为100秒。然后将这个配置应用到httpPost
对象上。
设置请求体为JSON字符串:
httpPost.setEntity(new StringEntity(jsonParams, ContentType.APPLICATION_JSON));
这行代码设置了HTTP POST请求的请求体。jsonParams
是传入的JSON格式参数,它被转换成StringEntity
,并设置其内容类型为ContentType.APPLICATION_JSON
,表示发送的是JSON格式的数据。
添加头部参数:
if (headParams != null) {
for (Map.Entry<String, String> entry : headParams.entrySet()) {
httpPost.addHeader(entry.getKey(), entry.getValue());
}
}
如果headParams
(请求头参数)不为空,则遍历这个Map,并将每个键值对添加到HTTP请求的头部。
执行请求并获取响应:
response = httpClient.execute(httpPost);
使用httpClient
执行httpPost
请求,并接收响应。响应被保存在CloseableHttpResponse
对象response
中。
检查响应状态码:
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "HTTP请求失败,状态码:" + statusCode);
}
从响应中获取HTTP状态码,如果状态码不是200(HttpStatus.SC_OK
),则抛出业务异常,表示HTTP请求失败。
获取响应二进制数据:
HttpEntity entity = response.getEntity();
byte[] responseBytes = EntityUtils.toByteArray(entity);
//这里补充一下,正常我们返回的是字符串,使用
//result = EntityUtils.toString(entity, "UTF-8");即可,我这个请求是有需要返回二进制数据,所以采用这样的方法,你可以根据需要自行改写获取响应体的格式并返回。
从响应中提取HttpEntity
,然后使用EntityUtils.toByteArray(entity)
将其转换为字节数组。这些字节是服务器返回的原始二进制数据。
异常处理:
catch (Exception e) {
log.error("执行HTTP POST请求异常:", e);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "执行HTTP POST请求异常:" + e.getMessage());
}
如果在执行过程中出现任何异常,将捕获这些异常,记录错误日志,并抛出业务异常。
关闭资源:
finally {
HttpClientUtils.closeQuietly(response);
HttpClientUtils.closeQuietly(httpClient);
}
在finally
块中,无论请求成功还是异常,都会关闭响应和客户端实例以释放资源。
这段代码的执行流程涵盖了创建客户端、配置请求、发送请求、处理响应和异常,是一个完整的用于发送HTTP POST请求并接收二进制响应的实现。
开发的过程中踩了一个坑,不能多次转换数据,转换一下,流已经关闭了,不能又一次读取数据
java.io.IOException: Attempted read from closed stream.
出现这个原因通常是因为尝试从已经关闭的流中读取数据。在你的代码中,这个问题很可能是由于以下原因造成的:
重复读取响应体
: 在你的代码中,你首先使用 EntityUtils.toString(entity) 读取了响应体作为字符串,然后又尝试通过 EntityUtils.toByteArray(entity) 从相同的 HttpEntity 实例中读取数据。HttpEntity 只能被读取一次,读取后流就会关闭。因此,第二次尝试读取时会抛出 Attempted read from closed stream 异常。
解决方案
要解决这个问题,你可以只读取一次响应体。如果你需要同时获取响应体的字符串表示和字节数组表示,可以先读取为字节数组,然后将字节数组转换为字符串,而不是直接从HttpEntity中读取两次。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。