如何使用apache HttpClient传递响应体

时间:2014-12-09 03:58:37

标签: java httpclient apache-httpclient-4.x apache-commons-httpclient

有一个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 个答案:

答案 0 :(得分:7)

编辑2

所以,如果你对线程/ runnables / Handler不熟悉并且不熟悉android AsyncTask,我会直接使用HttpUrlConnection(使用apacheHttpClient删除整个练习因为,基本上googl说HttpUrlConn将支持stream'd响应和它确实有效!)

对于转储标题等所有细节可能并不那么容易。但是,使用正常的流式响应对象,我认为它应该正常工作....请参阅编辑3以获取UrlConn代码示例

<强> EndEdit2

不清楚使用什么'stream'协议(渐进式dwnld | http流)或者你如何实际管理客户端的流式响应。

建议从连接中转储标头,以确切了解客户端和服务器之间的协议?

假设您关闭了UI线程(在asyncTask中或在Handler的回调部分中),因此您可能需要重构一点。

假设使用apache client 4.3.5+

的http流

如果响应的标题中没有长度,那么你在Http 1.1上做了一个'chunked'响应,你必须读取一个缓冲区直到你得到一个'last-chunk'或者决定关闭这个流或连接:

服务器刚刚开始发送(流),客户端应该根据生成实体内容的详细apache说明,通过使用缓冲区来处理它从http响应中获取的'input-stream'。

如果30秒的套接字超时会抢占活动流,我不记得了吗?请记住,在apache中,构建器中存在用于套接字超时和READ超时的单独设置。不希望socket关闭,并且不希望在服务器提供响应时等待可读流的可用字节超时。

无论如何,客户端处理程序只需要通过检查读入缓冲区的内容来了解​​流的结束方式......

如果适当的协议是“继续”&amp; “chunked”然后客户端上的响应处理程序应该在流处理程序循环中UNTIL它从http spec看到LAST-CHUNK。

 response.getEntity().getContent() 

应该为您提供处理响应流所需的引用,直到'last-chunk'...

我认为你应该read here关于如何使用一个缓冲的实体,其中需要多个读取才能在响应中的'last-chunk'结束。它是HttpUrlConn可能更容易的另一个原因......

执行一个处理缓冲读取的循环,直到由匹配'last-chunk'的字节表示的END。

然后根据有关消费实体和可重用连接的详细apache说明CLO流或CONN。

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

在'handler's callback或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();}

}

答案 1 :(得分:0)

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

示例:

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();