当前位置:   article > 正文

Java Springboot发送Http请求返回二进制数据及原理分析_springboot 字符串以二进制流形式返回

springboot 字符串以二进制流形式返回

Java Springboot发送Http请求返回二进制数据及原理分析

1、依赖

 <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4

2、工具类

/**
     * @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);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

3、原理分析

3.1基础流程

  1. HTTP 协议简介: HTTP(超文本传输协议)是一个用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是一个基于请求-响应模式的无状态协议,通常运行在TCP/IP协议之上。
  2. 客户端和服务器模型: 在HTTP通信中,通常有一个客户端(例如一个Web浏览器或一个HTTP客户端库,如Apache HttpClient)和一个服务器(托管Web资源的服务器)。客户端发起请求,服务器响应请求。
  3. 域名解析: 当客户端要发送HTTP请求时,首先需要解析服务器的域名(如果提供的是域名而非IP地址)。这涉及到DNS(域名系统)查询,将域名转换为IP地址。
  4. 建立TCP连接: HTTP通常运行在TCP协议之上。在发送HTTP请求之前,客户端会与服务器建立一个TCP连接。这个过程通常包括三次握手,确保双方都准备好进行数据传输。
  5. 发送HTTP请求: 一旦TCP连接建立,客户端就会通过这个连接发送一个HTTP请求。这个请求包含:
    • 请求行:包含方法(GET、POST等)、请求的资源的URI和HTTP版本。
    • 请求头:包含关于客户端环境和请求体的元数据,例如User-AgentAcceptContent-Type等。
    • 请求体:对于某些类型的请求(如POST),这部分包含要发送给服务器的数据。
  6. 服务器处理请求: 服务器接收到HTTP请求后,会根据请求的资源、方法和其他参数处理请求。这可能涉及到访问数据库、执行服务器端程序等操作。
  7. 发送响应: 处理请求后,服务器会向客户端发送一个HTTP响应。这个响应包含:
    • 状态行:包含HTTP状态码和状态消息,表示请求成功、失败或其他状态。
    • 响应头:包含关于服务器、响应体和资源的信息,如Content-TypeContent-Length等。
    • 响应体:包含请求的资源或其他数据。
  8. 关闭TCP连接: 数据传输完成后,TCP连接可以被关闭,或者根据HTTP头部中的Connection字段保持开放以供未来的请求复用。
  9. 无状态性和连接管理: HTTP是一个无状态协议,意味着每个请求都是独立的,服务器不保存两个请求之间的状态。但在现代HTTP应用中,通常使用cookies、会话等机制来维护状态。同时,持久连接、管道化等技术被用于优化连接管理和网络资源的使用。

3.2代码解析

  1. 创建HTTP客户端实例:

    CloseableHttpClient httpClient = HttpClients.createDefault();
    
    • 1

    这行代码使用Apache HttpClient库创建了一个CloseableHttpClient对象。这个客户端用于执行HTTP请求。createDefault()方法会返回一个默认配置的CloseableHttpClient实例。

  2. 创建HTTP POST请求对象:

    HttpPost httpPost = new HttpPost(url);
    
    • 1

    通过HttpPost类创建一个POST请求对象。这个对象包含了要发送到服务器的请求信息,其中url是请求的目标地址。

  3. 配置请求和传输超时时间:

    RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100000).setConnectTimeout(100000).build();
    httpPost.setConfig(requestConfig);
    
    • 1
    • 2

    使用RequestConfig设置请求的超时配置。setSocketTimeout设置从服务器读取数据的超时时间,setConnectTimeout设置连接服务器的超时时间。这里两者都设置为100秒。然后将这个配置应用到httpPost对象上。

  4. 设置请求体为JSON字符串:

    httpPost.setEntity(new StringEntity(jsonParams, ContentType.APPLICATION_JSON));
    
    • 1

    这行代码设置了HTTP POST请求的请求体。jsonParams是传入的JSON格式参数,它被转换成StringEntity,并设置其内容类型为ContentType.APPLICATION_JSON,表示发送的是JSON格式的数据。

  5. 添加头部参数:

    if (headParams != null) {
        for (Map.Entry<String, String> entry : headParams.entrySet()) {
            httpPost.addHeader(entry.getKey(), entry.getValue());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果headParams(请求头参数)不为空,则遍历这个Map,并将每个键值对添加到HTTP请求的头部。

  6. 执行请求并获取响应:

    response = httpClient.execute(httpPost);
    
    • 1

    使用httpClient执行httpPost请求,并接收响应。响应被保存在CloseableHttpResponse对象response中。

  7. 检查响应状态码:

    int statusCode = response.getStatusLine().getStatusCode();
    if (statusCode != HttpStatus.SC_OK) {
        throw new BusinessException(ErrorCode.SYSTEM_ERROR, "HTTP请求失败,状态码:" + statusCode);
    }
    
    • 1
    • 2
    • 3
    • 4

    从响应中获取HTTP状态码,如果状态码不是200(HttpStatus.SC_OK),则抛出业务异常,表示HTTP请求失败。

  8. 获取响应二进制数据:

    HttpEntity entity = response.getEntity();
    byte[] responseBytes = EntityUtils.toByteArray(entity);
    //这里补充一下,正常我们返回的是字符串,使用    
    //result = EntityUtils.toString(entity, "UTF-8");即可,我这个请求是有需要返回二进制数据,所以采用这样的方法,你可以根据需要自行改写获取响应体的格式并返回。
    
    • 1
    • 2
    • 3
    • 4

    从响应中提取HttpEntity,然后使用EntityUtils.toByteArray(entity)将其转换为字节数组。这些字节是服务器返回的原始二进制数据。

  9. 异常处理:

    catch (Exception e) {
        log.error("执行HTTP POST请求异常:", e);
        throw new BusinessException(ErrorCode.SYSTEM_ERROR, "执行HTTP POST请求异常:" + e.getMessage());
    }
    
    • 1
    • 2
    • 3
    • 4

    如果在执行过程中出现任何异常,将捕获这些异常,记录错误日志,并抛出业务异常。

  10. 关闭资源:

    finally {
        HttpClientUtils.closeQuietly(response);
        HttpClientUtils.closeQuietly(httpClient);
    }
    
    • 1
    • 2
    • 3
    • 4

    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中读取两次。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/431058
推荐阅读
相关标签
  

闽ICP备14008679号