首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何使用apache HttpClient流式传输响应正文

如何使用apache HttpClient流式传输响应正文
EN

Stack Overflow用户
提问于 2014-12-09 11:58:37
回答 2查看 33.6K关注 0票数 10

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

代码语言:javascript
运行
复制
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();
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-12-13 23:11:13

编辑2个

因此,如果你对线程/runnables/Handler和android AsyncTask感到不舒服,我会直接转到HttpUrlConnection (放弃使用Apache HttpClient的整个练习,因为基本上谷歌说HttpUrlConnection将支持流响应,它确实有效!)

这可能不是那么容易的所有细节,如转储标头。但是对于一个普通的流响应对象,我认为它应该可以正常工作...有关HttpsUrlConnection代码示例,请参阅编辑3

EndEdit2

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

建议转储来自连接的标头,以查看客户端和服务器到底达成了什么协议?

我假设您已经脱离了UI线程(无论是在AsyncTask中还是在处理程序的回调部分中);如果这不准确,您可能需要稍微重构一下。

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

如果响应头中没有长度,那么您就是在HTTP 1.1上执行“分块”响应,此时您必须读取缓冲区,直到获得“last-chunk”,或者决定关闭流或连接:

服务器刚刚开始发送(流),而客户端应该按照生成实体内容的详细说明,通过使用缓冲区来处理它从HTTP响应中获得的‘输入流’。

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

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

如果协议是"continue“& "chunked”,那么客户端上的响应处理程序应该处于流处理程序循环中,直到它看到来自http spec的最后一个块。

代码语言:javascript
运行
复制
 response.getEntity().getContent() 

应该会给你需要的引用来处理响应流,直到'last-chunk'...

我认为你应该read here关于如何消费一个缓冲的实体,其中需要不止一次读取才能在响应中的“最后一个区块”结束。这也是为什么HttpURLConnection更容易的另一个原因。

执行一个处理缓冲读操作的循环,直到与'last-chunk‘匹配的字节发出END信号。

然后根据Apache关于使用实体和可重用连接的详细说明关闭流或连接。

在apache HttpClient中编辑流式响应的代码

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

代码语言:javascript
运行
复制
 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,但您可以使用适当的字节,因为这来自流speach示例,文本中的单词将被发送回此处的UI )

代码语言:javascript
运行
复制
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();}

}
票数 8
EN

Stack Overflow用户

发布于 2020-10-24 16:06:46

试试RxSON:https://github.com/rxson/rxson它利用JsonPath在响应到达时立即从响应中读取JSON流数据块,并在响应完成之前将其解析为java对象。

示例:

代码语言:javascript
运行
复制
String serviceURL = "https://think.cs.vt.edu/corgis/datasets/json/airlines/airlines.json";
   HttpRequest req = HttpRequest.newBuilder(URI.create(serviceURL)).GET().build();
   RxSON rxson = new RxSON.Builder().build();

   String jsonPath = "$[*].Airport.Name";
   Flowable<String> airportStream = rxson.create(String.class, req, jsonPath);
   airportStream
       .doOnNext(it -> System.out.println("Received new item: " + it))
       //Just for test
       .toList()
       .blockingGet();
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27371201

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档