当前位置: 首页 > 面试题库 >

如何使用Apache HttpClient流式传输响应主体

凤修筠
2023-03-14
问题内容

我需要执行一个没有长度的八位字节流的api。它只是实时数据流。我遇到的问题是,当我发出请求时,似乎试图在将信息读入输入流之前先等待内容的结尾,但是它没有看到内容的结尾和NoHttpResponse异常的超时。以下是我的代码的简化版本:

private static HttpPost getPostRequest() {
    // Build uri
    URI uri = new URIBuilder()
            .setScheme("https")
            .setHost(entity.getStreamUrl())
            .setPath("/")
            .build();

    // Create http http
    HttpPost httpPost = new HttpPost(uri);

    String nvpsStr = "";
    Object myArray[] = nvps.toArray();
    for(int i = 0; i < myArray.length; i ++) {
        nvpsStr += myArray[i].toString();
        if(i < myArray.length - 1) {
            nvpsStr += "&";
        }
    }

    // Build http payload
    String request = nvpsStr + scv + streamRequest + "\n\n";
    // Attach http data
    httpPost.setEntity(new StringEntity(URLEncoder.encode(request,"UTF-8")));

    return httpPost;
}

// Where client is simply
// private static final CloseableHttpClient client = HttpClients.createDefault();
private static runPostRequest (HttpPost request) {
    CloseableHttpResponse response = client.execute(request);
    try {
        HttpEntity ent = response.getEntity();
        InputStream is = ent.getContent();
        DataInputStream dis = new DataInputStream(is);
        // Only stream the first 200 bytes
        for(int i = 0; i < 200; i++) {
            System.out.println(( (char)dis.readByte()));
        }

    } finally {
        response.close();
    }
}

问题答案:

编辑2

因此,如果您对线程/可运行程序/处理程序不满意,而对Android
AsyncTask不满意,我将直接转到HttpUrlConnection(使用Apache
HttpClient删除整个练习,因为基本上Google表示HttpUrlConnection将支持流式响应,并且它确实工作!)

检测所有细节(例如转储标头)可能并不容易。但是,对于正常的流式响应对象,我认为它应该可以正常工作....参见HttpsUrlConnection代码示例的编辑3

EndEdit2

从问题中不清楚是使用什么“流”协议(渐进式下载或HTTP流),还是您实际上如何在客户端上管理流响应。

建议将标头从连接中转储以查看客户端和服务器的确切协议??

我假设您关闭了UI线程(在AsyncTask或Handler的回调部分中);如果这不准确,则可能需要重构一点。

假设HTTP流与Apache HttpClient 4.3.5+一起使用

如果响应的标题中没有长度,则您正在HTTP 1.1上执行“分块”响应,您必须在其中读取缓冲区,直到获得“最后一个块”或决定关闭流或连接为止:

服务器只是开始发送(流式传输),并且客户端应按照Apache关于 生成实体内容 的详细说明,使用缓冲区来处理它从HTTP响应中获得的“输入流”

我不记得30秒钟的套接字超时是否会抢占活动流吗?请记住,在Apache中,构建器中存在用于套接字超时和 读取
超时的单独设置。当服务器提供响应时,不想让套接字关闭,也不想等待可读流的可用字节超时。

无论如何,客户端处理程序只需要通过检查读入缓冲区的内容来知道流是如何结束的…

如果使用的协议是“ continue”和“ chunked”,则客户端上的响应处理程序应处于流处理程序循环中,直到从http
spec中看到LAST-CHUNK为止。

 response.getEntity().getContent()

应该为您提供处理响应流直到’last-chunk’所需的参考…

我认为您应该在这里阅读如何使用缓冲的实体,而在响应中的“最后一个块”结束时,将需要不止一次读取。这是HttpURLConnection可能更容易的另一个原因…

进行一个循环处理缓存的读取,直到由匹配“ last-chunk”的字节发出的END信号为止。

然后根据有关使用实体和可重用连接的详细Apache注释,关闭流或连接。

*用于Apache HttpClient中流式响应的 *EDIT 代码

在“处理程序”的回调或asyncTask中

 request.execute();
...

 processStreamingEntity(response.getEntity());
 response.close();

//implement your own wrapper as mentioned in apache docs

    private void processStreamingEntity(HttpEntity entity) throws IOException {
        InputStreamHttpEntityHC4 bufHttpEntity = new InputStreamHttpEntityHC4(entity);
        while not bufHttpEntity.LAST_CHUNK {
            handleResponse(bufHttpEntity.readLine())
}

编辑3

如果您使用HttpURLConnection版本。(使用MessageHandler,但您可以在原处使用字节,因为这是来自流式传输示例,此处来自文本的单词被发送回UI)

private void openHttpsConnection(String urlStr, Handler mhandler) throws IOException {
    HttpsURLConnection httpConn = null;
    String line = null;
    try {
        URL url = new URL(urlStr);
        URLConnection urlConn = url.openConnection();               
        if (!(urlConn instanceof HttpsURLConnection)) {
            throw new IOException ("URL is not an Https URL");
        }               
        httpConn = (HttpsURLConnection)urlConn;
        httpConn.setAllowUserInteraction(false);
        httpConn.setInstanceFollowRedirects(true);
        httpConn.setRequestMethod("GET");
        httpConn.setReadTimeout(50 * 1000);
        BufferedReader is =
                new BufferedReader(new InputStreamReader(httpConn.getInputStream()));

        while ((line = is.readLine( )) != null) {

                Message msg = Message.obtain();
                msg.what=1;  
                msg.obj=line;                       
                mhandler.sendMessage(msg);

        }               
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch( SocketTimeoutException e){
        e.printStackTrace();
    } catch (IOException e) {

        e.printStackTrace();
        Message msg = Message.obtain();
            msg.what=2;
            BufferedInputStream in = new BufferedInputStream(httpConn.getErrorStream());

            line =new String(readStream(in));
            msg.obj=line;
            mhandler.sendMessage(msg);

    }
    finally {httpConn.disconnect();}

}


 类似资料:
  • 服务器是一个应用程序,具有路由处理程序,用于记录接收到的请求对象,并提供无限的结果流: 当上面的代码运行时,服务器记录请求对象的无限流,但客户端从不记录任何响应对象。 如果通过执行以下操作绑定请求流:,那么客户机在收到所有请求后开始记录响应。

  • 使用debezium从Postgres流式更改 已完成的设置: Docker设置。 启动Postgres、zookeeper、kafka和debezium Connector。 使用decoderbufs、wal2json(postgres)设置远程数据库。 使用curl连接到debezium。 创建了一个观察者。 问题:当我启动watcher时,它读取了之前发生的所有更改,但当任何插入完成时,k

  • 问题内容: 我正在玩弄所有花哨的node.js / mongodb / express平台,偶然发现一个问题: 您可能已经猜到了,对Mongodb 进行查询并返回游标 这会导致错误: 根据mongodb驱动程序doc,我试图将此转换器传递给: 但这无济于事。 谁能告诉我如何正确地将某些内容发送给响应? 还是唯一的解决方案是使用’data’和’end’事件手动抽取数据的样板? 问题答案: 这里的其他

  • 问题内容: 我有一个200MB的文件,想通过下载提供给用户。但是,由于我们希望用户仅下载一次此文件,因此我们这样做: 强制下载。但是,这意味着整个文件必须加载到内存中,这通常不起作用。我们如何以每块kb的速度将文件流式传输给他们? 问题答案: 尝试这样的事情

  • 我想知道是否有可能实现2种方式的流式传输使用Spring WebFlow?基本上,我希望让客户端发送服务器接收到的数据通量将它们映射到String,然后返回结果,所有这些都流利地进行,而无需收集数据。我使用RSocket完成了它,但我想知道我是否可以使用超文本传输协议2.0(使用Spring和Project-Retor)获得相同的结果。 试过这样做: 1-客户: 2.服务器: 或者: 或者: 没有

  • 我正在尝试编写同时使用gRPC和REST的服务。实现技术有Java、Spring-Boot和gRPC。使用场景示例如下: 目的是有外部客户端可以通过RESTendpoint和/或通过进行gRPC调用与应用程序交互。在内部,有“网关”服务提供外部接口,并负责在外部客户端和执行实际工作的“域”服务之间传输/路由请求和响应。内部服务将通过gRPC进行通信。 外部客户端不知道如何在内部处理事情,域服务没有